Java 泛型

泛型,在日常工作中,使用最广泛的就是集合及 Map 中,一直以来也没认真想过泛型,现在准备来学一下泛型。

来,先从最简单的开始,一个元素全部为 String 的集合开始。

ArrayList<String> nameList = new ArrayList<>();
nameList.add("张三");
nameList.add("李四");
nameList.add("王五");
System.out.println(nameList.toString());

我们一行一行看代码,第一行,是创建了一个泛型类型为 String 的 ArrayList 对象,往下三行分别向 nameList 中添加三个元素,最后进行打印。

  1. new ArrayList<>(); 这种写法是在 Java 1.7 及其之后才有的,在 Java 之前的版本,需要在 <> 内指定出具体的泛型类型,如:new ArrayList<String>(); 简写是因为做了类型推断。
  2. 实际的 Android 开发中,最好还是别用 System.out.println();,这里只是个人学习 Demo 才这么写。

输出内容:

[张三, 李四, 王五]

来,下面我们来自己创建一个泛型出来。为什么创建泛型呢?或者说我们创建什么泛型呢?实际上泛型可以作用于类、接口或是方法(函数)。

泛型类

   public class Rep<T> {
       private int code;
       private String message;
       private T result;
   }

这样的结构应该不会陌生,服务端在返回给客户端的数据就是按照这种形式进行组装的,其中 result 可以是任何类型的任何值,既有可能是 boolean 类型的 false,也有可能是 List 类型的集合。

实际应该还有其他的内容,如 get 、set 方法等,此处略过。

泛型接口

   public interface Shop<T> {
     T buy();
   
     int back(T product);
   }

定义了一个泛型的接口,内部包含两个没有方法体的方法。一个的返回值是泛型类型 T ,表示买了一个什么东西;一个的参数是泛型类型 T,表示一个什么东西退货退多少钱。这里的 什么东西 就是泛型类型 T。

泛型方法

   public T getResult() {
     return result;
   }
   
   public void setResult(T result) {
     this.result = result;
   }

这两个泛型方法就是简单的将泛型类中的 result 这个成员变量进行赋值和返回。

这好像没什么实际意义,来点有意思的东西。刚才我们定义了一个 Shop 的泛型接口,现在我们再来创建一个卖 Apple 的店(AppleShop,首先需要一个 Apple 的类,其次需要一个实现 Shop 接口的 AppleShop 类。

public class Apple {}

public class AppleShop implements Shop {
    @Override
    public Object buy() {
        return null;
    }

    @Override
    public int back(Object product) {
        return 0;
    }
}

唉,好像有点不对了,AppleShop 里面的 buy() 为什么返回的不是 Apple 对象还有那个 back方法为什么参数也成了 Object 而不是 Apple ?

原因在与:class AppleShop implements Shop 准确的来说是在于 implements Shop ,在实现泛型接口时,如果不指定具体的泛型类型(这里指的是 Apple)时,跟实现接口没什么区别,只是其中的泛型方法的返回值、参数全变成了 Object。

上面的 AppleShop 类相当于实现了下面这个接口。

public interface Shop {
    Object buy();

    int back(Object product);
}

那我想让他实现我想要的 Apple 类型的怎么做呢?在实现类的类名后 <类型名称> 即可。AppleShop 的正确写法如下:

public class AppleShop implements Shop<Apple> {
    @Override
    public Apple buy() {
        return new Apple();
    }

    @Override
    public int back(Apple product) {
        return 1;
    }
}

其中,buy 方法直接返回一个 Apple 对象,back 方法返回 1 元。

现在又有一家店既能买东西,又能修东西,它得先继承 Shop,我这回知道了,就这么写就行。

public interface Repairableable extends Shop<T>{
    
}

唉,不对,咋报错呢?

image-20190627171053335

因为这里的 T 是不存在的。不存在?咋不存在,刚才不是在 Shop 这个接口里面都写了吗?怎么还是不存在?为什么呀。不存在?人家都是那么用 T 这个关键字,人家都不错,我错了?冷静下来想想,Shop 接口中那个 T 是用 <> 包裹着然后紧跟着接口名,我也试试。

public interface Repairableable<T> extends Shop<T>{

}

哎,对了,不报错了,这是什么原因?

首先,T 不是一个关键字,不是因为 T 才让一个借口变成泛型接口,它只是一个符号,重要的是包裹着 T 的 <>你可以把 T 换成任何字符,比如 B,E,甚至是 String 这个单词,它只是一直对未知类型的指代。

其次,T 的作用范围仅仅只是当前类或是当前接口。

所以,我们在 Repairableable 这个接口名后再像 Shop 接口那样声明出 T 就行了。

  1. 当你用 String 作为泛型类型的指代,那与本类中用来表示字符串的 String 冲突时,需要把表示字符串的 String 类的包名进行指定。
  2. Repairableable<T> extends Shop<T> 这两个 T 虽然字符一样,但是表示意思不一样,前者表示这是一个类型参数是 T 的接口名是 Repairableable 的接口,后者表示的是,Shop 接口的类型参数的实际值是 T 的接口。

对了,我们常用的 HashMap 他是两个泛型参数,那个怎么去定义的呢?

public class HashMap<K,V>{
  public void put(K key, V value) {
  }
  
  public V get(Object key) {
      return v;
  }
}

非 HashMap 源代码,经改动。

来自己动手写一下。

现在有一家店不仅卖苹果,还卖手机卡,这手机卡,有移动、联通、电信这三家运营商,他到底买的哪家的呢?也不知道,也不清楚。

public class ChinaMobile {
}

public class ChinaTelecom {
}

public class ChinaUnicom {
}

public interface SimShop<T, S> extends Shop<T>  {
    S buySim(String name, String id);
}

public class SimAppleShop implements AppleSimShop<Apple, ChinaMobile> {
    @Override
    public ChinaMobile buySim(String name, String id) {
        return null;
    }

    @Override
    public Apple buy() {
        return null;
    }

    @Override
    public int back(Apple product) {
        return 0;
    }
}

哎,这样看起来是挺不错的哈,泛型挺好用的呀。不过在实现 SimShop 的时候,要是有人乱填,写了一个一个不是三家运营商的类怎么办?那系统不就报错了?

当然为了,应对这种情况,Java 给出了解决方法,在 SimShop 的接口声明上加上限制,具体是这样的:

public interface SimShop<T, S extends Sim> extends Shop<T>  {
    S buySim(String name, String id);
}

这样一来,在实现 SimShop 类时,填入的第二个类型参数必须是继承自 Sim 类或实现了 Sim 接口的类。这里的 extends 指的不是继承,而是限制其类型参数的 类型。如果需要限制多个内容,使用 & 符合进行连接,但不能存在多个类,只能存在多个接口,因为 Java 不允许多继承。在多限制条件中,存在类,需要放在最左边。例如:

interface SimShop <T, S extends View & Sim & Fruit & Serializable>