Java 泛型

泛型

  • 泛型开始出现在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());

image-20210919115201088

  • 在完成类型安全检测后,会进行泛型的擦除,对象进入和离开方法的边界处添加类型检查和类型转换的方法(所以本质还是当作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>());
      
    • image-20210919115101186

    • 输出集合的所有方法,我们返现,返回值是可传入的最高类型Object,符合下界通配符的定义

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值