# 泛型
# 常用的字母
常用的 T,E,K,V,?
- ? 表示不确定的 java 类型
- T (type) 表示具体的一个java类型
- K V (key value) 分别代表java键值中的Key Value
- E (element) 代表Element
- 但是泛型的参数只能是类类型,不能是基本的数据类型,他的类型一定是自Object的
WARNING
泛型不接受基本数据类型,换句话说,只有引用类型才能作为泛型方法的实际参数
# 无界通配符?
我有一个超类Animal和几个子类,如狗,猫,鹦鹉,等…现在我需要一个动物的列表,我的第一个想法是像:
List<Animal> listAnimals
相反,我的同事推荐像:
List<? extends Animal> listAnimals
通配符在你声明局部变量时没有什么意义,但是当你为一个方法声明一个参数时,它们是非常重要的。 想象一下你有一个方法:
int countLegs ( List< ? extends Animal > animals )
{
int retVal = 0;
for ( Animal cur : animals )
{
retVal += cur.countLegs( );
}
return retVal;
}
2
3
4
5
6
7
8
9
10
有了这个签名,你可以这样做:
List<Dog> dogs = ...;
countLegs( dogs );
List<Cat> cats = ...;
countLegs( cats );
List<Animal> zoo = ...;
countLegs( zoo );
2
3
4
5
6
7
8
然而,如果你声明countLegs像这样:
int countLegs1 ( List< Animal > animals )
然后在前面的示例中,只有countLegs(动物园)将编译,因为只有该调用具有正确的类型。
所以,对于不确定或者不关心实际要操作的类型,可以使用无限制通配符(尖括号里一个问号,即 <?> ),表示可以持有任何类型。像 countLegs 方法中,限定了上届,但是不关心具体类型是什么,所以对于传入的 Animal 的所有子类都可以支持,并且不会报错。而 countLegs1 就不行。
# 上界通配符< ? extends E>
上界:用 extends 关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的子类。
- 如果传入的类型不是 E 或者 E 的子类,编译不成功
- 泛型中可以使用 E 的方法,要不然还得强转成 E 才能使用
private <K extends A, E extends B> E test(K arg1, E arg2){
E result = arg2;
arg2.compareTo(arg1);
//.....
return result;
}
2
3
4
5
6
类型参数列表中如果有多个类型参数上限,用逗号分开
# 下界通配符< ? super E>
下界: 用 super 进行声明,表示参数化的类型可能是所指定的类型,或者是此类型的父类型,直至 Object
# ? 和 T 的区别
?和 T 都表示不确定的类型,区别在于我们可以对 T 进行操作,但是对 ? 不行
// 可以
T t = operate();
// 不可以
? car = operate();
2
3
4
5
T 是一个 确定的 类型,通常用于泛型类和泛型方法的定义,?是一个 不确定 的类型,通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法。
# 区别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)
2
3
4
5
6
7
# 区别2:类型参数可以多重限定而通配符不行
public static <T extends MultiA & MultiB> void test(T t){}
使用 & 符号设定多重边界(Multi Bounds),指定泛型类型 T 必须是 MultiLimitInterfaceA 和 MultiLimitInterfaceB 的共有子类型,此时变量 t 就具有了所有限定的方法和属性。对于通配符来说,因为它不是一个确定的类型,所以不能进行多重限定。
# 区别3:通配符可以使用超类限定而类型参数不行
类型参数 T 只具有 一种 类型限定方式:
T extends A
但是通配符 ? 可以进行 两种限定:
? extends A
? super A
2
# Class<T> 和 Class<?> 区别
Class<T> 在实例化的时候,T 要替换成具体类。Class<?> 它是个通配泛型,? 可以代表任何类型,所以主要用于声明时的限制情况。比如,我们可以这样做申明:
// 可以
public Class<?> clazz;
// 不可以,因为 T 需要指定类型
public Class<T> clazzT;
2
3
4
那如果也想 public Class<T> clazzT; 这样的话,就必须让当前的类也指定 T ,
public class Test3<T> {
public Class<?> clazz;
// 不会报错
public Class<T> clazzT;
2
3
4
# 泛型类
泛型类的定义只要在申明类的时候,在
类名后面直接加上<E>,中的E可以是任意的字母,也可以多个,多个用逗号隔开就可以。示例代码如下
public class SelfList<E> {}
public class SelfList<T> {}
2
# 泛型类中的实际类型的推断
那么什么时候确定这个E 的具体类型呢?其实是在
new一个对象的时候指定的,请看下面代码
public class SelfList<E> {
public void add(E e) {
if (e instanceof String) {
System.out.println(" I am String");
} else if (e instanceof Integer) {
System.out.println("I am Integer");
}
}
public static void main(String[] args) {
//这里创建的时候指定了String
SelfList<String> a = new SelfList<>();
a.add("123");
//这里创建的时候指定了Integer
SelfList<Integer> b = new SelfList<>();
b.add(123);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
为什么实例方法可以,而静态方法却报错?
- 首先告诉你结论:静态方法不能使用类定义的泛型,而是应该单独定义泛型
- 到这里估计很多小伙伴就瞬间明白了,因为静态方法是通过类直接调用的,而普通方法必须通过实例来调用,类在调用静态方法的时候,后面的泛型类还没有被创建,所以肯定不能这么去调用的
# 泛型接口
泛型接口和类的使用方式一样
public interface IndicatorTypeService<T> {}
//这里定义的时候指定具体类型为ProductSale,当然也可以这里没有指定具体类型
public class ProductSaleServiceImpl implements IndicatorTypeService<ProductSale> {}
2
3
4
# 泛型方法
//注意这是个假的泛型方法,不要以为有一个E就是泛型方法哦
public void add(E e) {
if (e instanceof String) {
System.out.println(" I am String");
} else if (e instanceof Integer) {
System.out.println("I am Integer");
}
}
2
3
4
5
6
7
8
# 泛型方法的定义
public <T> T get(T t) {
return t;
}
2
3
返回值和public 之间的<T>是泛型方法的必要条件,并且这个和类的定义的泛型E 是可以同名(一般设置不同名),并且他们之间是独立的。<T>可以多个,多个用逗号隔开,列如<T,V>- 返回值不一定是T,可以是任意的类型,如Long
- 方法中的参数也不一定是T,可以是任意的类型,如Long。只是
泛型方法一般返回值类型和参数有其中一个是定义的泛型(全是具体类型就没意义了)
public <T> T get(T t) {
return t;
}
public static void main(String[] args) {
//这里创建的时候指定了String
SelfList<String> a = new SelfList<String>();
a.add("123");
int num = a.get(123);
}
2
3
4
5
6
7
8
9
# 泛型方法中的实际泛型的推断
那么泛型方法是怎么确定这个具体类型的呢?
主要思想是在
调用该泛型方法传进去的参数类型和返回值类型来确定具体类型的
- 泛型变量在
参数列表中只出现一次,调用该方法时根据传进的实参类型来确定
public <T> T get1(T t) {
if (t instanceof String) {
System.out.println(" I am String");
} else if (t instanceof Integer) {
System.out.println("I am Integer");
}
return t;
}
public static void main(String[] args) {
//这里调用的时候传进去的是int 类型,所以确定了他的类型是int
int b=a.get1(123);
}
2
3
4
5
6
7
8
9
10
11
12
13
- 当参数列表中有
多个参数使用了相同的泛型变量,返回值类型也使用了该变量,那么返回值类型由他们的公共父类来决定最终的泛型类型
public <T> T get2(T t, T t2) {
if (t instanceof Float) {
System.out.println(" I am String");
} else if (t instanceof Integer) {
System.out.println("I am Integer");
} else if (t instanceof Number) {
System.out.println("I am Number");
}
return t;
}
public static void main(String[] args) {
//这里返回值类型必须是number 类型
Number b = a.get2(123, 12.1);
}
//输出:I am Integer
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
因为根据第一条规则,传进去的是什么类型就是什么类型,但是返回值类型候需要根据第二条规则来确定
# 反射中的泛型使用
上面说的都是在编译之前就可以确定的泛型。
大家知道,泛型运行的时候其实是会被擦除的。不过没关系,还是提供给我们通过反射的方式来获取。首先我来认识下java中的泛型类型继承结构
ParameterizedType-->Type<--TypeVariable
# ParameterizedType
public interface ParameterizedType extends Type {
/**
* 返回一个实际类型的数组
* 比如对于ArrayList<T>,被实例化的时候ArrayList<String>,这里返回的就是String
*
*/
Type[] getActualTypeArguments();
/**
* 这里其实就是返回去掉泛型后的真实类型
* 对于List<T> 这里返回就是List
*/
Type getRawType();
/**
* 这里针对的是内部类的情况,返回的是他的外层的类的类型
* 例如SelfHashMap 里面有一个Entry 内部类,也就是SelfHashMap.Entry<String,String>
* 返回的就是SelfHashMap
*/
Type getOwnerType();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# ParameterizedType测试验证
public class TypeTest {
public static class TestMap {
public static class Entry<T,V extends Collection>{}
}
public static class Ht extends TestMap.Entry<Integer, List<String>>{}
@Test
public void testTypeP(){
Class clazz = Ht.class;
Type type = clazz.getGenericSuperclass();
if (type instanceof ParameterizedType){
ParameterizedType ParameterizedType = (ParameterizedType) type;
System.out.println(Arrays.toString(ParameterizedType.getActualTypeArguments()));
System.out.println(ParameterizedType.getOwnerType());
System.out.println(ParameterizedType.getRawType());
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//输出
[class java.lang.Integer, java.util.List<java.lang.String>]
class com.chlm.mysession.common.TypeTest$TestMap
class com.chlm.mysession.common.TypeTest$TestMap$Entry
2
3
# TypeVariable
public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {
/**
* 这里返回的是泛型的上界类型数组,比如 <T, V extends Collection> 这里的上界类型就是Collection
*/
Type[] getBounds();
/**
* 返回的是声明该泛型变量的类,接口,方法等
* 列如 public static class Entry<T, V extends Collection> 返回的就是Entry
*/
D getGenericDeclaration();
/**
* 这里返回的就是泛型定义的变量名称,比如 <T> 返回的就是T
*
*/
String getName();
/**
* 这里返回的就是AnnotatedType 数组,jdk1.8 才加进来,本文不分析,直接跳过
*/
AnnotatedType[] getAnnotatedBounds();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
测试类
@Test
public void testVariable(){
Class clazz = TestMap.Entry.class;
TypeVariable[] typeVariables = clazz.getTypeParameters();
Stream.of(typeVariables).forEach(typeVariable -> {
System.out.println(typeVariable.getName());
System.out.println(ArrayUtils.toString(typeVariable.getAnnotatedBounds()));
System.out.println(ArrayUtils.toString(typeVariable.getBounds()));
System.out.println(typeVariable.getGenericDeclaration());
System.out.println("我是完美分割线");
});
}
2
3
4
5
6
7
8
9
10
11
12
T
{sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl@3fb6a447}
{class java.lang.Object}
class com.chlm.mysession.common.TypeTest$TestMap$Entry
我是完美分割线
V
{sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl@79698539}
{interface java.util.Collection}
class com.chlm.mysession.common.TypeTest$TestMap$Entry
我是完美分割线
2
3
4
5
6
7
8
9
10