泛型
-
泛型开始出现在jdk 1.5 因为集合容器的需要,泛型可以在编译时进行类型判断。
-
类型参数化
类型由原来的具体化转为类似参数传入,在需要使用时再使用传入的参数类型。
一、注意事项
1、泛型只在编译时有效
ArrayList<Student> students=new ArrayList<>();
ArrayList<Result<String>> results=new ArrayList<>();
System.out.println(students.getClass());
System.out.println(results.getClass());
- 在完成类型安全检测后,会进行泛型的擦除,对象进入和离开方法的边界处添加类型检查和类型转换的方法(所以本质还是当作Object 在用吧)
- 泛型信息不会进入运行时
二、泛型使用
1、泛型类
-
public class NormalGeneric<T> { private T name; public T getName() { return name; } public void setName(T name) { this.name = name; } }
-
class 类名 <泛型标志> { private 泛型标志 name; public 泛型标志 getName() { return name; } public void setName(泛型标志 name) { this.name = name; } }
-
常用的泛型标识
-
T、E、K、V
-
-
泛型的真实参数类型必须为Object的派生类
-
instanceof关键字不能作用在 类型参数已经确认的
-
Result<String> result=new Result<>(); if(result instanceof Result<String>);
-
2、泛型类的继承
-
public class SubGenericClass extends NormalGeneric{ @Override public Object getName() { return super.getName(); } }
-
在继承使用了泛型的父类时如果不使用 泛型标志,将默认使用
Object
为类型 编译器会提示 **参数化类(泛型类)**的原始引用 -
public class SubGenericClass<T> extends NormalGeneric<T>{ @Override public T getName() { return super.getName(); } }
-
子类使用的泛型标识必须和父类的相同,否则会出现编译错误 , 无法确定类型
3、泛型接口
-
public class NormalInterfaceImplClass implements NormalGenericInterface{ @Override public Object generateName() { return null; } }
-
同泛型类的继承一样,泛型接口通常被使用在 工厂模式中
-
我们可以使用参数化类型定义一个泛型接口传入多种类型
-
public class NormalInterfaceImplClass implements NormalGenericInterface<String>{ @Override public String generateName() { return null; } }
-
在传入具体的参数时,当然除了手动定义,我们还可以动态传入
-
NormalGenericInterface<ArrayList<String>> genericInterface=new NormalInterfaceImplClass<>();
4、泛型通配符
-
泛型标识也属于泛型通配符
- 上界通配符
< ? extends E>
- 上界通配符
-
public class Animals { public void eat(){ System.out.println("every animal can eat"); }; } public class Dog extends Animals{ } public class Horse extends Animals{ }
-
创建了一个简单的例子
-
public class MyTest { public static void main(String[] args) { ArrayList<Dog> dogs=new ArrayList<>(); dogs.add(new Dog()); dogs.add(new Dog()); ArrayList<Horse> horse=new ArrayList<>(); horse.add(new Horse()); horse.add(new Horse()); testMethod(dogs); testMethod(horse); } public static void testMethod(ArrayList<? extends Animals> animals) { animals.forEach(Animals::eat); } }
-
通配符在变量声明的时候可能起到的作用和你的想法相违背,但它在作为参数类型时的作用非常重要
-
当我们不关心具体类型的方法或参数时我们可以使用
?
通配符 定义参数传入具体类型- 下界通配符
< ? super E>
- 传入的参数必须是 表示参数化的类型可能是所指定的类型,或者是此类型的父类型,一直传递到Object
- 下界通配符
-
public static void main(String[] args) { testSuper(new ArrayList<Animals>(),new ArrayList<Dog>()); } public static <T> void testSuper(ArrayList<? super T> animals,ArrayList<T> dag) { System.out.println("test"); }
? super T
当 T 被具体的参数所替换后 ,我们需要传入的类型一定要比T 大
上界通配符主要用于读数据,下界通配符主要用于写数据
4、 ?和 T等
-
public <T extends Number> void test(List<T> dest, List<T> src)
T
等标识符 可以用来限定类型的一致
-
test(List<? extends Number> dest, List<? extends Number> src)
?
通配符是 不确定的,或只能做出部分限制,所以这个方法不能保证两个 List 具有完全相同的元素类型
4、泛型方法
- 泛型方法的泛型参数独立于泛型类之外
- 泛型类的泛型方法可使用 和本身所在泛型类不同的泛型标识
- 也可以使用和 泛型类相同的泛型标识符 但是可以传入和泛型类不同的具体参数类型
- 静态方法如果要使用泛型必须定义为泛型方法 (即使在泛型类中 因为静态方法不属于类,所以无法确定类型)
5、泛型的擦除
-
public static void main(String[] args) { NormalGeneric<String> normalGeneric = new NormalGeneric<>(); //这里使用的私有类型 Class<? extends NormalGeneric> normalGenericClass = normalGeneric.getClass(); for (Field field : normalGenericClass.getDeclaredFields()) { System.out.println(field.getType()); } }
- 通过反射在运行时查看
normalGeneric
的类型 是Object - 泛型的引入并没有对java 底层做出太多的改变,在我看来很大一部分是为了减少默认的
object
在使用时出现的错误 在编译器的类型警告可以让我们有效的避免这些
- 通过反射在运行时查看
-
<? extendes T>
-
上界限定擦除
-
public class LowerBoundGeneric<T extends Number> { T t; }
-
LowerBoundGeneric<Integer> integerLowerBoundGeneric=new LowerBoundGeneric<>(); for (Field field : integerLowerBoundGeneric.getClass().getDeclaredFields()) { System.out.println(field.getType()); }
-
在使用上界通配符进行泛型的限定时 最后将被擦除到父类的类别
-
-
<? super T>
-
下界限定擦除
-
public class UpperBoundGeneric{ public <T> void test(ArrayList<? super T> t) { System.out.println(t.getClass()); for (Method method : t.getClass().getMethods()) { System.out.println(method.getName()+":"+method.getReturnType()); } } }
-
UpperBoundGeneric upperBoundGeneric = new UpperBoundGeneric(); upperBoundGeneric.test(new ArrayList<Integer>());
-
-
输出集合的所有方法,我们返现,返回值是可传入的最高类型
Object
,符合下界通配符的定义
-