概念
jdk5.0引入参数化类型概念,在创建集合时指定聚合元素的类型,比如List<String>,表明该List只能保存字符串类型的对象;这个String就是泛型
允许在定义类、接口时通过一个标识,表示类中某个属性的类型或某个方法的返回值或参数的类型(例如,继承或实现这个接口、创建对象或调用方法时)确定(即传入实际的类型参数,也称为类型实参)
集合框架在声明接口和其实现类时,使用了泛型(jdk5.0)
在实例化集合对象时:
如果没有使用泛型,则认为操作的是Object类型的数据
如果使用了泛型,则需要指明泛型的具体类型。一旦指明了泛型的具体类型,则在集合的相关方法中,凡事使用泛型的位置,都替换为具体的泛型类型
示例
public void test() {
List<Integer> list = new ArrayList<Integer>();
// 当添加非Integer类型数据时,会编译报错
list.add(1);
// 两种方式都可以
HashMap<String, Integer> map = new HashMap<String, Integer>();
// jdk7的新特性,类型推断
HashMap<String, Integer> map1 = new HashMap<>();
map1.put("tom", 70);
map1.put("jack", 80);
// 两种方式定义的参数结果相同
Set<Map.Entry<String, Integer>> enterySet = map1.enterySet();
Iterator<Map.Entry<String, Integer>> iterator enterySet.iterator();
// 类型推断
var enterySet = map1.enterySet();
var iterator enterySet.iterator();
}
比较器中使用
class Product {
private double price;
public Product(double price) {
this.price = price;
}
}
// 排序
public void test() {
Comparator<Product> comparator = new Comparator() {
@Override
public int compare(Product o1, Product o2) {
return Double.compare(o1.price, o2.price);
}
};
TreeSet<Product> t = new TreeSet(comparator);
t.add(new Product(0.19));
t.add(new Product(0.10));
t.add(new Product(0.20));
Iterator iterator = t.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
class Product implements Comparable<Product> {
private double price;
public Product(double price) {
this.price = price;
}
@Override
public int compareTo(Product o) {
if (o == this) {
return 0;
}
return Double.compare(this.price, o.price);
}
}
public void test() {
TreeSet<Product> t = new TreeSet();
t.add(new Product(0.19));
t.add(new Product(0.10));
t.add(new Product(0.20));
Iterator iterator = t.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
自定义泛型类/接口
格式
// T不是固定,可以自己随意设置
class A<T> {
}
interface A<T1,T2> {
}
// 示例
class A<T> {
T t;
public A(T t) {
this.t = t;
}
}
注意
泛型参数在指明时,是不可以使用基本数据类型的,可以使用包装类
声明了类的泛型参数后,就可以在类的内部(属性、方法、构造器)使用此泛型参数
如果在创建自定义泛型类的对象是,没有指明泛型参数类型,那么泛型将被擦除,泛型对应的类型均按照Object处理,但是不等价于Object
除创建泛型类对象外,子类继承泛型类时,实现类实现泛型接口时,也可以确定泛型结构中泛型的类型
如果在泛型类提供子类时,子类也不确定泛型的类型,则可以继续使用泛型参数
还可以在现有父类的泛型参数的基础上,新增泛型参数
不可以在静态方法中使用类的泛型
异常类不能是带泛型的
泛型继承
// 这种情况下B不是泛型类
public class B extends A<Integer> {
}
public class B extends A<T> {
}
// 这时获取从类B获取父类A的参数时,如果参数类型是泛型,那么获得的值就是指定的类型
// 这种情况下B是泛型类
public class B<E> extends A<Integer> {
}
public class B<T> extends A<T> {
}
public class B<T,E> extends A<T> {
}
自定义泛型方法
格式
// 需要使用<E>修饰声明,表示是一个泛型,E不是固定的,可以自己指定
public <E> E method(E e) {
}
public String method(<E> E e) {
}
public static <E> E method(E e) {
}
// 调用方法时需要指定参数类型
注意
泛型方法可以声明为泛型方法
泛型方法所属类可以是泛型类,也可以不是泛型类
泛型继承的体现
类SuperA是类A的父类,则G<SuperA> 与 G<A> 的关系:
G<SuperA> 和 G<A> 是并列的两个类,没有任何子父类的关系
SuperA是类A的父类或结果,则SuperA<G> 与 A<G> 的关系:
SuperA<G> 与 A<G>有继承或实现的关系
即A<G>的实例可以赋值给SuperA<G>类型的引用(或变量)
通配符?
说明
G<?> 可以看做时G<A>的父类,即可以将G<A>的对象赋值给G<?>类型的引用(或变量)
读取数据:允许,读取的值是Object类型
写入数据:不允许,可以写入null值
示例
List<?> l1 = null;
List<String> l2 = null;
List<Integer> l3 = null;
l1 = l2;
l1 = l3;
l1.get(0); // 允许
// l1.add("a"); // 不允许
有限制条件的通配符
List<? extends A>
可以将List<A>获取List<B>赋值给List<? extends A>,其中B类是A类的子类;如果B类是A类的父类则不可以赋值
可以读取,读取到的是指定类型数据
不可以写入,允许写入null
List<? super B>
可以将List<A>获取List<B>赋值给List<? super A>,其中B类是A类的父类;如果B类是A类的子类则不可以赋值
可以读取,读取到的是Object类型数据
可以写入指定类型的子类,不能写入Object
示例
public class A extends C {
}
public class B extends A {
}
public class C extends D {
}
public class D {
}
public class F extends B {
}
public void test() {
List<? extends A> l = null;
List<B> l1 = new ArrayList<>();
List<C> l2 = new ArrayList<>();
l = l1;
// l = l2; //不允许
// l.add(new B()); // 不允许
// l.add(new F()); // 不允许
List<? super A> e = null;
List<B> e1 = new ArrayList<>();
List<D> e2 = new ArrayList<>();
// e = e1; // 不允许
e = e2; // 允许
e.add(new A()); // 允许
e.add(new B()); // 允许
// e.add(new Object()); // 不允许
}