JAVA基础之泛型
2016-02-15 21:39
639 查看
概念
什么是泛型?1.Jdk 1.5出现的安全机制 2.泛型实现了参数化类型的概念,使代码可以应用于多种类型 3.使用泛型可以在编译期防止将错误类型的对象放入容器中
好处
1.将运行时期的问题ClassCastException转到了编译时期 2.避免了强制转换麻烦
使用场景 - 符号:<>
当操作的引用数据类型不确定的时候,就使用<>,将要操作的引用数据类型传入即可。 <>其实就是一个用于接收具体引用数据类型的参数范围。
在程序中,只要用到了带有<>的类或接口,就要明确传入的具体引用数据类型
引入泛型的一个原因就是为了创建容器类, 泛型的主要目的之一就是用来指定容器要持有什么类型的对象,而且有编译器来保证类型的正确性
JAVA泛型的核心概念:告诉编译器想使用什么类型,然后编译器帮你处理一切细节
泛型 – 简单泛型
public class Test { public static void main(String[] args) { Holder<Person> mHolder = new Holder<Person>(); mHolder.set(new Worker()); System.out.println(mHolder.get()); } } class Holder<T> { T t; public T get() { return t; } public void set(T t) { this.t = t; } } class Person { @Override public String toString() { return this.getClass().getSimpleName(); } } class Worker extends Person {} // 输出:Worker
泛型 – 泛型类
元祖将一组对象直接打包存储于其中的一个单一对象,这个容器对象允许读取其中元素,但是不允许向其中存放新的对象。
public class Test { public static void main(String[] args) { TwoTuple<Integer, String> two = new TwoTuple<Integer, String>(1, "android"); ThreeTuple<Integer, Character, String> three = new ThreeTuple<Integer, Character, String>( 2, 'b', "java"); System.out.println(two); System.out.println(three); } } class TwoTuple<A, B> { public final A first; public final B second; public TwoTuple(A a, B b) { first = a; second = b; } @Override public String toString() { return "First:" + first + " Second:" + second; } } class ThreeTuple<A, B, C> extends TwoTuple<A, B> { public final C third; public ThreeTuple(A a, B b, C c) { super(a, b); third = c; } @Override public String toString() { return "First:" + first + " Second:" + second + " Third:" + third; } } // 输出: // First:1 Second:android // First:2 Second:b Third:java
// 不使用LinkedList实现内部存储机制 class Stack<T> { private Node<T> top = new Node<T>(); public void push(T item) { top = new Node<T>(item, top); } public T pop() { T result = top.item; if (!top.end()) { top = top.next; } return result; } private static class Node<U> { U item; Node<U> next; Node() { item = null; next = null; } Node(U item, Node<U> next) { this.item = item; this.next = next; } boolean end() { return item == null && next == null; } } }
泛型类
在jdk1.5后,使用泛型来接收类中要操作的引用数据类型,称为泛型类
当类中的操作的引用数据类型不确定的时候,就使用泛型来表示
public class Test { public static void main(String[] args) { Tools<Student> mTools = new Tools<Student>(); mTools.setObject(new Student()); // mTools.setObject(new Worker()); 泛型可以提供安全性 Student mStudent = mTools.getObject(); } } class Tools<T> { T object; public T getObject() { return object; } public void setObject(T object) { this.object = object; } } class Person{} class Student extends Person{} class Worker extends Person{}
泛型 – 泛型接口
泛型也可以应用于接口。例如生成器,这是一种专门负责创建对象的类。
泛型接口:将泛型定义在接口上。
public class Test { public static void main(String[] args) { InterImpl in = new InterImpl(); in.show("Test"); InterImpl2<Integer> in2 = new InterImpl2<Integer>(); in2.show(8); } } interface Inter<T> { public void show(T t); } class InterImpl implements Inter<String> { public void show(String str) { System.out.println("InterImpl:" + str); } } class InterImpl2<Q> implements Inter<Q> { public void show(Q q) { System.out.println("InterImpl2:" + q); } }
泛型 – 泛型方法
泛型方法:定义在泛型上的方法
是否拥有泛型方法与其所在的类是否是泛型没有关系。
泛型方法使得该方法能够独立于类而产生变化
基本指导原则:无论何时,只要你能做到,就应该尽量使用泛型方法。
static方法无法访问泛型类的类型参数,如果static方法需要使用泛型能力,就必须使其成为泛型方法。
定义泛型方法
只需将泛型参数列表置于返回值之前。
public class Test { public static void main(String[] args) { Tools<String> mTools = new Tools<String>(); mTools.show1("Test"); mTools.show2(0.5f); mTools.show2(0.5d); Tools.show4(8); Map<Integer, String> map= Tools.getMap(); map.put(1, "Hello"); mTools.show2(map); Tools<Map<String, String>> tool = new Tools<Map<String, String>>(); // tool.show1(Tools.getMap()); // Tools<Map<String,String>> is not applicable for the arguments (Map<Object,Object>) // 类型参数推断只对赋值操作有效。如果将结果作为参数传递,那么类型参数会被识别为Object。 tool.show1(Tools.<String,String>getMap()); // 需要显示的类型说明 } } class Tools<T> { // 泛型类方法 public void show1(T t) { System.out.println(t.getClass().getSimpleName()); } // 泛型方法 public <M> void show2(M m) { System.out.println(m.getClass().getSimpleName() + " : " + m); } // 泛型方法 public <X, L> void show3(X x, L l) { System.out.println(x.getClass().getSimpleName() + " " + l.getClass().getSimpleName()); } // 泛型静态方法 public static <N> void show4(N n) { System.out.println(n.getClass().getSimpleName()); } // 泛型静态方法,类型参数推断 public static <K, V> Map<K, V> getMap() { return new HashMap<K, V>(); } } /** 输出: String Float : 0.5 Double : 0.5 Integer HashMap : {1=Hello} */
可变参数和泛型方法
可变参数与泛型方法能够很好地共存
public class Test { public static void main(String[] args) { List<String> list = Tool.createList("Hello","World"); System.out.print(list); } } class Tool { public static <T> List<T> createList(T... args) { List<T> list = new ArrayList<T>(); for (T t : args) { list.add(t); } return list; } } // [Hello, World]
通用的类生成器 - 能使用生成器生成对象类的特点:
1.必须声明为public 2.必须具备默认的构造器
public interface Generator<T> { T next(); } public class BaseGenerator<T> implements Generator<T> { private Class<T> type; public BaseGenerator(Class<T> type) { this.type = type; } @Override public T next() { try { return type.newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } public static <T> Generator<T> create(Class<T> type) { return new BaseGenerator<>(type); } } public class Person { @Override public String toString() { return getClass().getSimpleName(); } } public class Test { public static void main(String[] args) { Generator<Person> generator = BaseGenerator.create(Person.class); System.out.println(generator.next()); } }
泛型可应用于内部类以及匿名内部类
public interface Generator<T> { T next(); } public class Test { public static void main(String[] args) { Queue<Customer> queue = new LinkedList<Customer>(); for (int i = 0; i < 5; i++) { queue.add(Customer.generator().next()); } System.out.println(queue); } } class Customer { private static long counter = 0; private final long id = counter++; private Customer() { } @Override public String toString() { return getClass().getSimpleName() + " " + id; } public static Generator<Customer> generator() { return new Generator<Customer>() { @Override public Customer next() { return new Customer(); } }; } } // [Customer 0, Customer 1, Customer 2, Customer 3, Customer 4]
利于泛型构建复杂的模型
public interface Generator<T> { T next(); } public class Test { public static void main(String[] args) { System.out.println(new Store(2, 2, 5)); } } class Product { private final int id; private String description; private double price; public Product(int _id, String _des, double _price) { id = _id; description = _des; price = _price; } @Override public String toString() { return id + " : " + description + ", price:$" + price; } public static Generator<Product> generator() { return new Generator<Product>() { private Random random = new Random(47); @Override public Product next() { return new Product(random.nextInt(1000), "Test", Math.round(random.nextDouble() * 1000) + 0.99); } }; } } //货架 class Shelf extends ArrayList<Product> { public Shelf(int nProducts) { Generator<Product> generator = Product.generator(); for (int i = 0; i < nProducts; i++) { add(generator.next()); } } } //过道 class Aisle extends ArrayList<Shelf> { public Aisle(int nShelves, int nProducts) { for (int i = 0; i < nShelves; i++) { add(new Shelf(nProducts)); } } } //商店 class Store extends ArrayList<Aisle> { public Store(int nAisles, int nShelves, int nProducts) { for (int i = 0; i < nAisles; i++) { add(new Aisle(nShelves, nProducts)); } } @Override public String toString() { StringBuilder result = new StringBuilder(); for (Aisle aisle : this) { for (Shelf shelf : aisle) { for (Product product : shelf) { result.append(product); result.append("\n"); } } } return result.toString(); } } /** 输出 258 : Test, price:$400.99 861 : Test, price:$160.99 ... */
泛型 – 擦除 & 补偿
public class Test { public static void main(String[] args) { ArrayList<Integer> list1 = new ArrayList<Integer>(); ArrayList<String> list2 = new ArrayList<String>(); System.out.println(list1.getClass() == list2.getClass()); } } /** 输出 true */
// 在泛型代码内部,无法获取任何有关泛型参数类型的信息 // 因此,只知道诸如类型参数标示符和泛型类型边界这类信息,无法知道用来创建某个特定实例的实际类型参数。 public class Test { public static void main(String[] args) { List<Frob> list = new ArrayList<Frob>(); Map<Frob, Fnorkle> map = new HashMap<Frob, Fnorkle>(); Quark<Frob> person = new Quark<Frob>(); Particle<Integer, String> teacher = new Particle<Integer, String>(); // getTypeParameters()方法将返回TypeVariable(类型参数)数组 System.out.println(Arrays.toString(list.getClass().getTypeParameters())); System.out.println(Arrays.toString(map.getClass().getTypeParameters())); System.out.println(Arrays.toString(person.getClass().getTypeParameters())); System.out.println(Arrays.toString(teacher.getClass().getTypeParameters())); } } class Frob {} class Fnorkle {} class Quark<Q> {} class Particle<POS,MON> {} /** 运行结果: [E] [K, V] [Q] [POS, MON] */
泛型的擦除
擦除主要的正当理由是从非泛化代码到泛化代码的转变过程,以及不破坏现有类库的情况下,将泛型融入Java语言。
泛型技术是给编译器使用的技术,用于编译时期,确保了类型的安全。
运行时,会将泛型去掉,生成的class文件中是不带泛型的,这个称为泛型的擦除。
为什么擦除呢?因为为了兼容运行的类加载器。
擦除代价
泛型不能用于显示地引用运行时类型的操作之中,如转型、instanceof操作,和new 表达式。所有关系参数的类型信息都丢失了。
class Erased<T> { private static final int SIZE = 100; public void function(Object object) { // ERROR ,擦除会将类型信息都擦除掉 // 因此不能使用instanceof,new等 // if (object instanceof T) {} // T t = new T(); // T[] arr= new T[SIZE]; T[] obj = (T[]) new Object[SIZE]; } } //泛型中使用newInstance()创建对象 public class Test { public static void main(String[] args) { ClassAsFactory<Person> factory = new ClassAsFactory<Person>(Person.class); System.out.println(factory.obj); } } class ClassAsFactory<T> { T obj; public ClassAsFactory(Class<T> kind) { try { // 需要有默认构造器,否则将抛出InstantiationException obj = kind.newInstance(); } catch (Exception e) { System.out.println(e); } } } class Person { @Override public String toString() { return getClass().getSimpleName(); } } /** 运行结果: Person */
泛型数组
不能创建泛型数组,一般的解决方案是在任何想要创建泛型数组的地方都使用ArrayList
因为有了擦除,数组的运行时类型就只能是Object[]。 如果我们立即将其转型为T[],那么在编译期该数组的实际类型就将丢失,编译器可能会错过某些潜在的错误检查。 正因为这样,最好是在集合内部使用Object[],然后当你使用数组元素时,添加一个对T的转型
public class Test { static final int SIZE = 10; static Generic<Integer>[] gia; @SuppressWarnings("unchecked") public static void main(String[] args) { // java.lang.ClassCastException // gia = (Generic<Integer>[])new Object[SIZE]; gia = (Generic<Integer>[]) new Generic[SIZE]; System.out.println(gia.getClass().getSimpleName()); gia[0] = new Generic<Integer>(); // cannot convert from Object to Generic<Integer> // gia[1] = new Object(); // cannot convert from Object to Generic<Integer> // gia[2] = new Generic<Double>(); } } class Generic<T> {} /** * 运行结果: Generic[] */
public class Test { public static void main(String[] args) { GenericArray<Integer> gai = new GenericArray<Integer>(10); // ClassCastException: Object cannot be cast to Integer // Integer[] ia = gai.rep(); Object[] objects = gai.rep(); } } class GenericArray<T> { private T[] array; @SuppressWarnings("unchecked") public GenericArray(int size) { array = (T[]) new Object[size]; } public void put(int index, T item) { array[index] = item; } public T get(int index) { return array[index]; } public T[] rep() { return array; } }
public class Test { public static void main(String[] args) { GenericArray<Integer> gai = new GenericArray<Integer>(10); for (int i = 0; i < 10; i++) { gai.put(i, i); } for (int i = 0; i < 10; i++) { System.out.print(gai.get(i) + " "); } System.out.println(); try { Integer[] ia = gai.rep(); } catch (Exception e) { System.out.println(e.toString()); } } } class GenericArray<T> { private Object[] array; public GenericArray(int size) { array = new Object[size]; } public void put(int index, T item) { array[index] = item; } @SuppressWarnings("unchecked") public T get(int index) { return (T) array[index]; } @SuppressWarnings("unchecked") public T[] rep() { return (T[]) array; } } /** * 运行结果: 0 1 2 3 4 5 6 7 8 9 java.lang.ClassCastException */ -----------------------
// 数组对象可以保留有关它们包含的对象类型的规则。 // 可以使用泛型容器将这种错误检测移入到编译期 public class Test { public static void main(String[] args) { Fruit[] fruits = new Apple[10]; try { fruits[0] = new Fruit(); } catch (Exception e) { System.out.println(e.toString()); } try { fruits[1] = new Pear(); } catch (Exception e) { System.out.println(e.toString()); } } } class Fruit {} class Apple extends Fruit {} class Pear extends Fruit {} /** * 运行结果: java.lang.ArrayStoreException: com.test.Fruit java.lang.ArrayStoreException: com.test.Pear */
泛型的补偿
在运行时,通过获取元素的类型进行转换动作,不用使用者再强制转换了
泛型 – 边界
边界使得你可以在用于泛型的参数类型上设置限制条件。 尽管这使得你可以强制规定泛型可以应用的类型,但是其潜在的一个更重要的效果是你可以按照自己的边界类型来调用方法。
擦除移除了类型信息,所以用无界泛型参数调用的方法只可以使用Object调用的方法。 如果将这个参数限制为某个类型的子集,那么就可以用这些类型子集来调用方法。
注意:通配符被限制为单一边界
class Person {} class Student extends Person {} class Workder extends Person {} class PersonTool<T extends Person> { T person; public PersonTool(T person) { this.person = person; } public T getPerson() { return person; } }
public class Test { public static void main(String[] args) { Solid<Bounded> solid = new Solid<Bounded>(new Bounded()); solid.getColor(); solid.getItem(); } } interface HasColor { java.awt.Color getColor(); } interface Weight { int weight(); } class Dimension { public int x, y, z; } class Bounded extends Dimension implements HasColor, Weight { public int weight() { return 10; } public Color getColor() { return null; } } class Solid<T extends Dimension & HasColor & Weight> { T item; public Solid(T item) { this.item = item; } T getItem() { return item; } int getX() { return item.x; } int getY() { return item.y; } int getZ() { return item.z; } int weight() { return item.weight(); } java.awt.Color getColor() { return item.getColor(); } }
泛型的通配符 - 无界通配符<?>
boolean containsAll(Collection<?> c)
public class Test { public static void main(String[] args) { List<? extends Fruit> list = new ArrayList<Apple>(); // list不能添加任何Object类型 // ArrayList<Apple>会被向上转型为List<? extends Fruit> // 因此list对应的add方法为:add(? extends Fruit), // 编译器并不知道? extends Fruit是什么类型,因此不能添加任何元素 // contains(Object o)/indexOf(Object o)不涉及通配符,因此不受影响 // list.add(new Fruit()); // list.add(new Apple()); // list.add(new Pear()); list.add(null); list = new ArrayList<Apple>(Arrays.asList(new Apple(), new Apple())); System.out.println(list.get(0)); System.out.println(list.contains(new Apple())); System.out.println(list.indexOf(new Apple())); } } class Fruit { @Override public String toString() { return getClass().getSimpleName(); } } class Apple extends Fruit {} class Pear extends Fruit {} /** 输出: Apple false -1 */
public class Test { public static void main(String[] args) { ArrayList<String> al1 = new ArrayList<String>(); al1.add("Hello"); al1.add("World"); ArrayList<Integer> al2 = new ArrayList<Integer>(); al2.add(1); al2.add(2); printCollection(al1); printCollection(al2); } public static void printCollection(Collection<?> collection) { Iterator<?> iterator = collection.iterator(); while(iterator.hasNext()) { System.out.println(iterator.next()); } } }
泛型的限定 - 上限<? extends E>:接收E类型或E的子类型对象
一般在存储元素的时候都是使用上限,因为这样取出都是按照上限类型来运算,不会出现类型安全隐患
boolean addAll(Collection<? extends E> c);
泛型的限定 - 下限<? super E>:接收E类型或E的父类型对象
通常对集合中的元素进行取出操作时,可以用下限
TreeSet(Comparator<? super E>)
public class Test { public static void main(String[] args) { ArrayList<Worker> al1 = new ArrayList<Worker>(); ArrayList<Student> al2 = new ArrayList<Student>(); printCollection1(al1); printCollection1(al2); printCollection2(al2); } public static void printCollection1(Collection<? extends Person> collection) { Iterator<? extends Person> iterator = collection.iterator(); while(iterator.hasNext()) { Person person = iterator.next(); System.out.println(person.getName()); } } public static void printCollection2(Collection<? super Student> collection) { Iterator<? super Student> iterator = collection.iterator(); while(iterator.hasNext()) { System.out.println(iterator.next()); } } } class Person { String getName() { return ""; } } class Worker extends Person {} class Student extends Person {}
常见问题
任何基本类型都不能作为类型参数,不能创建ArrayList<int>之类的东西创建ArrayList<Integer>,并将基本类型应用于这个容器,那么自动包装机制将自动实现int到Integer的双向转换。
自动包装机制不能应用于数组。
public class Test { public static void main(String[] args) { Holder<String> holder = new Holder<String>("JAVA"); CaptureConversion.f2(holder); // 自动包装机制 Holder<Integer> holder2 = new Holder<Integer>(1); CaptureConversion.f2(holder2); int[] nums = {1, 2, 3}; Integer[] integers = {1, 2, 3}; // 自动包装机制不能应用于数组 // Holder<Integer[]> holder3 = new Holder<Integer[]>(nums); Holder<Integer[]> holder3 = new Holder<Integer[]>(integers); CaptureConversion.f2(holder3); } }
// 捕获转换,将不确定的类型转换为确定类型 class CaptureConversion { static <T> void f1(Holder<T> holder) { T t = holder.get(); System.out.println(t.getClass().getSimpleName()); } static void f2(Holder<?> holder) { f1(holder); } } class Holder<T> { T value; public Holder(T val) { value = val; } public void set(T val) { value = val; } public T get() { return value; } } /** 运行结果: String Integer Integer[] */
一个类不能实现同一个泛型接口的两种变体
由于擦除的原因,这两个变体会成为相同的接口
interface Payable<T> {} class Employee implements Payable<Employee> {} // Hourly 不能编译通过 class Hourly extends Employee implements Payable<Hourly> {} interface NewPayable {} class NewEmployee implements NewPayable {} //Hourly 能编译通过 class NewHourly extends NewEmployee implements NewPayable {}
转型和警告
使用带有泛型类型参数的转型或instanceof不会有任何效果。
class FixedSizeStack<T> { private int index = 0; private Object[] storage; public FixedSizeStack(int size) { storage = new Object[size]; } public void push(T item) { storage[index++] = item; } @SuppressWarnings("unchecked") public T pop() { return (T) storage[--index]; // Type safety: Unchecked cast from Object to T // 由于转型原因,编译器无法知道这个转型是否安全 } }
重载
由于擦除的原因,重载方法将产生相同的类型签名 当被擦除的参数不能产生唯一的参数列表时,必须提供明显有区别的方法名
class UserList<W, T> { //void f(List<W> wlist) {} //void f(List<T> tlist) {} void f1(List<W> wlist) {} void f2(List<T> tlist) {} }
基类劫持了接口
class ComparablePet implements Comparable<ComparablePet> { public int compareTo(ComparablePet o) { return 0; } } // The interface Comparable cannot be implemented more than once with different // arguments: Comparable<ComparablePet> and Comparable<Cat> class Cat extends ComparablePet implements Comparable<Cat> {} class Dog extends ComparablePet { @Override public int compareTo(ComparablePet o) { return super.compareTo(o); } }
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树