背景: 泛型太痛苦了,无中生有.从0构建,比喻效果.
我: ? <> E T ,这些东西太让人头痛了,就是抽象的东西,就算使用3天,我也要把这个东西弄清楚,否则极为难受.
小殊 💙 : 有什么我可以帮你的吗? 我会在一旁听你讲的.
我: 好的,一步一步来.不着急.我必须弄清楚了.
我: start —>
名称解释:
泛型:广泛的类型
通配符:通用匹配的符号
就像橡皮泥一样. <?>
// 不可以
?car = operate();
为什么会这样呢?底层原理是什么?
T 是一个 确定的 类型,通常用于泛型类和泛型方法的定义,?是一个 不确定 的类型,通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法。
? 不是一个类型,举个例子.T是橡皮泥,?是水. 上尚若水,水是无形的. 可以幻化任何形状,就如同灵魂一般.就是一种能量.
可以使用玄幻比喻吗? 好,按照这个确定,我们开始对代码的演绎.
我有一个父类 Animal 和几个子类,如狗、猫等,现在我需要一个动物的列表,我的第一个想法是像这样的:
List<Animal> listAnimals
但是老板的想法确实这样的:
List<? extends Animal> listAnimals
static int countLegs (List<? extends Animal > animals ) {
int retVal = 0;
for ( Animal animal : animals )
{
retVal += animal.countLegs();
}
return retVal;
}
static int countLegs1 (List< Animal > animals ){
int retVal = 0;
for ( Animal animal : animals )
{
retVal += animal.countLegs();
}
return retVal;
}
public static void main(String[] args) {
List<Dog> dogs = new ArrayList<>();
// 不会报错
countLegs( dogs );
// 报错
countLegs1(dogs);
}
所以,作用明白了吗? 水比橡皮泥更加强大.水用来方法里面. 每一个抽象的东西,必须给一个具象的对象进行对应.
<? extends Animal> 这是代表继承动物的类吗? ,是的. 水只能幻化动物的形状,这个72变,36变极为相似.
dog继承了动物. dog是儿子,所以就是 ? 增加了子类, supper增加了父类? 多了可以调用的类型.
private <'K' extends A, E extends B> E test(K arg1, E arg2){
E result = arg2;
arg2.compareTo(arg1);
//.....
return result;
}
’ 是因为没有办法所以添加的
这个操作是为了,增加多个的限制范围.这很像数轴.以添加.
dst 类型 “大于等于” src 的类型,这里的“大于等于”是指 dst 表示的范围比 src 要大,因此装得下 dst 的容器也就能装 src 。
上界通配符主要用于读数据,下界通配符主要用于写数据。
区别1:通过 T 来 确保 泛型参数的一致性
// 通过 T 来 确保 泛型参数的一致性 public <T extends Number> void test(List<T> dest, List<T> src) //通配符是 不确定的,所以这个方法不能保证两个 List 具有相同的元素类型 public void test(List<? extends Number> dest, List<? extends Number> src)
不保证两个list具有相同元素是什么意思? 上面代码就不能保证泛型的作用,需要强制转换,所以使用?不合理?
区别2:类型参数可以多重限定而通配符不行
使用 & 符号设定多重边界(Multi Bounds),指定泛型类型 T 必须是 MultiLimitInterfaceA 和 MultiLimitInterfaceB 的共有子类型,此时变量 t 就具有了所有限定的方法和属性。
对于通配符来说,因为它不是一个确定的类型,所以不能进行多重限定。
为什么不能多重限定? 可以单限定,因为java不允许多重继承吗? 接口是可以多重继承的.没有关系.
extends 后面如果是接口. 代表什么,这些接口的子类(接口),包括实现类吗?
接口是可以在后面加 = 类. 这种操作的.
区别3:通配符可以使用超类限定而类型参数不行
类型参数 T 只具有 一种 类型限定方式:
T extends A
但是通配符 ? 可以进行 两种限定:
? extends A ? super A
Class’’ 和 Class’<?>’ 区别
// 通过反射的方式生成 multiLimit // 对象,这里比较明显的是,我们需要使用强制类型转换 MultiLimit multiLimit = (MultiLimit) Class.forName("com.glmapper.bridge.boot.generic.MultiLimit").newInstance();
对于上述代码,在运行期,如果反射的类型不是 MultiLimit 类,那么一定会报 java.lang.ClassCastException 错误。
对于这种情况,则可以使用下面的代码来代替,使得在在编译期就能直接 检查到类型的问题
public class Test3{
public static <T> T createInstance(Class<T> clazz) throws IllegalAccessException, InstanttationException{
return clazz.newInstance();
}
public static void main(String[] args) throws IllegalAccessException, InstanttationException{
A a=createInstance(A.class);
B b=createInstance(B.class);
}
}
class A{}
class B{}
这段代码就比较复杂了,慢慢分析一波. 泛型方法.
T 什么操作? 类型是一个泛型? 假如 返回的是一个 a的对象,所以,这一块 T 就是类似String ,前面必须加上,否则是不是有点不伦不类,这个地方不像普通的写一个类型. 其实很简单, 这种单独使用,很少的.
使用比喻来说: <>是什么意思? 过滤器
( <> T )两个,总是觉得一脸懵逼.一定要确认清楚.
Class A.class .每个class都是泛型,因为类型肯定不确定. 为什么不使用Class 呢? 不能代表.所有类型吗?
有什么深意?
Class a 这个和 List ls 对比一下.
a.newInstance();
ls.get()
List 和 Class 一样,就是一种类型 <>泛型,简单的组合.
任重道远呀. 有一点眉目,但是. 还是很混乱.
看这个,是不是有很多问号.
/**
* 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
* 即:class FruitGenerator<T> implements Generator<T>{
* 如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class"
*/
class FruitGenerator<T> implements Generator<T>{
@Override
public T next() {
return null;
}
}
这是声明类的.所以没有 T.
泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。
//同时这也印证了泛型通配符章节所描述的,?是一种类型实参,可以看做为Number等所有类的父类
泛型类中的泛型方法 只有一个这种东西
class GenerateTest<T>{
public void show_1(T t){
System.out.println(t.toString());
}
//在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型。可以类型与T相同,也可以不同。
//由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型,编译器也能够正确识别泛型方法中识别的泛型。
public <E> void show_3(E t){
System.out.println(t.toString());
}
}
可变参数的学习
public <T> void printMsg( T... args){
for(T t : args){
Log.d("泛型测试","t is " + t);
}
}
T… 是不是有很多问号