泛型主要作用:
类型检查报错,类型转型,类型约束
通配符 ?:
【子类的泛型类型对象不可以赋值给父类的泛型声明】例如:
ArrayList<Fruit>fruits=new ArrayList<Apple>()//集合中,不可行
Shop<Fruit> shop = new Shop<Apple>()//对象声明创建,不可行
但使用通配符可以解除这个限制【ArrayList<? extend Fruit>fruits = new ArrayList<Apple>()】
【协变covariant】? extend :表示定义了上界,放宽泛型声明要求,但是只能读数据,不能添加数据,例如:
1 ArrayList<? extend Fruit> fruits = new ArrayList<Apple>()
2 Banana banana = new Banana()
3 fruits.add(banana)//不可行,无法编译通过
4 fruits.get(0)//可行
在ide中,第3行就不会被编译通过。
何时使用? extend,一般用于场景功能或者三方库提供方法,例如:
//提供一个公共方法供外部使用
float getTotalWeight(List<? extend Fruit> fruits){
float weight = 0;
for(Fruit fruit:fruits){
weight+=fruit.getWeight();//只能读取数据
}
return weight;
}
//以上方法中,形参使用了? extend,调用的地方就可以传入List<Apple>或者List<Banana>等包含Fruit子类的参数,而不仅仅局限于某一个特定的参数
【逆变contravariant】? super:表示定义下界,只能添加数据,不能读取数据,例如:
1 List<? super Apple>apples = new ArrayList<Fruit>()
2 apples.add(new Apple())//可行
3 Apple apple = apples.get(0)//不可行,无法编译通过
何时使用? super,也是场景化功能,例如:
//在某个类中,有一个方法需要传List数组,如果该数组里面的泛型参数可以是此类的父类,就可以用? super
//例如在Apple.java中,有以下方法
public void addToList(List<? super Apple>list){
list.add(this);//只能添加数据
}
//方法addToList就可以传入Apple的父类的List,而不局限于List<Apple>
//例如以下使用情况
List<Fruit>fruits = new ArrayList<Fruit>()
addToList(fruits)
泛型擦除(java为了兼容性和性能,选择了擦除):
在java代码里面,类型擦除是泛型的特质,所有泛型在运行的时候,定义泛型的地方会被“擦除”掉,
在数组中:
1 Fruit[] fruits = new Apple[5]
2 fruits[0] = new Banana()
3 Apple apple = fruits[0]
运行的时候,第2行(添加)就已经报错了,因为不允许往Apple数组中添加Banana元素
但在ArrayList中如果有以下代码(强转):
1 ArrayList<Apple> apples = new ArrayList<Apple>()
2 ArrayList<Fruit> fruitList = (ArrayList) apples
3 fruitList.add(new Banana());
4 Apple apple = apples .get(0)
在ide中会编译通过,但是在运行时第4行会报错,但是第3行(添加)会通过,实际上第3行就不被允许,但是存在泛型擦除的情况,第3行却被运行通过了,使我们在第一时间未能发现错误,但是在使用的时候(第4行),就会出现错误的,但在数组中,是可以在添加的时候就发现错误;所以,在泛型的使用中,可能会因为类型擦除导致我们不能在第一时间发现问题,从而就会出现类型不安全的问题。【父类的泛型声明类型,不能创建子类的值,例如:ArrayList<Fruit> fruits = new ArrayList<Apple>(),是不被允许的】,所以? extend这种通配符,就可以在编译期发现这种类型不安全的问题(添加数据的时候),但是在数组中,不存在泛型擦除情况,所以在运行过程中可以发现问题。
泛型方法
例如:findViewById这个方法,与对象本身无关
public <T extends View> T findViewById(@IdRes int id) {
return getDelegate().findViewById(id);
}
泛型类型推断(简化代码)
///val image:ImageView = findViewById<ImageView>(R.id.iv_image)
val image:ImageView = findViewById(R.id.iv_image)