java基础-泛型

2024-04-11

概念

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()); // 不允许

}


{/if}