12.1 泛型的概念

12.1.1 泛型的引进

例如:出产瓶子的厂家,一开始并不知道咱们将来会用瓶子装什么,咱们什么都能够装,可是有的时分,咱们在运用时,想要限制某个瓶子只能用来装什么,这样咱们不会装错,而用的时分也能够定心的运用,无需再三思量。咱们日子中是在运用这个瓶子时在瓶子上“贴标签” ,这样就轻松解决了问题。

【12】 泛型
还有,在Java中咱们在声明办法时,当在完成办法功能时假如有不知道的数据需求参与,这些不知道的数据需求在调用办法时才干确定,那么咱们把这样的数据经过形参表明。那么在办法体中,用这个形参名来代表那个不知道的数据,而调用者在调用时,对应的传入值就能够了。

【12】 泛型

受以上两点启发,JDK1.5设计了泛型的概念。泛型即为“类型参数”,这个类型参数在声明它的类、接口或办法中,代表不知道的通用的类型。例如:

java.lang.Comparable接口和java.util.Comparator接口,是用于目标比较巨细的标准接口,这两个接口仅仅限制了当一个目标大于另一个目标时回来正整数,小于回来负整数,等于回来0。可是并不确定是什么类型的目标比较巨细,之前的时分只能用Object类型表明,运用时既费事又不安全,因此JDK1.5就给它们增加了泛型。

public interface Comparable<T>{
  int compareTo(T o) ;
}
public interface Comparator<T>{
   int compare(T o1, T o2) ;
}

其中<T>便是类型参数,即泛型。

12.1.2 泛型的优点

示例代码:

JavaBean:圆类型

package com.atguigu.generic;
​
public class Circle{
  private double radius;
​
  public Circle(double radius) {
    super();
    this.radius = radius;
   }
​
  public double getRadius() {
    return radius;
   }
​
  public void setRadius(double radius) {
    this.radius = radius;
   }
​
  @Override
  public String toString() {
    return "Circle [radius=" + radius + "]";
   }
​
}

比较器

package com.atguigu.generic;
import java.util.Comparator;
public class CircleRadiusComparator implements Comparator{
    @Override
    public int compare(Object o1, Object o2) {
        //强制类型转换
        Circle c1 = (Circle) o1;
        Circle c2 = (Circle) o2;
        return Double.compare(c1.getRadius(), c2.getRadius());
    }
}

测验类

package com.atguigu.generic;
​
public class TestNoGeneric {
  public static void main(String[] args) {
    CircleRadiusComparator com = new CircleRadiusComparator();
    System.out.println(com.compare(new Circle(1), new Circle(2)));
​
    System.out.println(com.compare("圆1", "圆2"));//运转时异常:ClassCastException
   }
}

那么咱们在运用如上面这样的接口时,假如没有泛型或不指定泛型,很费事,而且有安全隐患。

由于在设计(编译)Comparator接口时,不知道它会用于哪品种型的目标比较,因此只能将compare办法的形参设计为Object类型,而实践在compare办法中需求向下转型为Circle,才干调用Circle类的getRadius()获取半径值进行比较。

运用泛型:

比较器:

package com.atguigu.generic;
​
import java.util.Comparator;
​
public class CircleComparator implements Comparator<Circle> {
​
  @Override
  public int compare(Circle o1, Circle o2) {
    //不再需求强制类型转换,代码更简练
    return Double.compare(o1.getRadius(), o2.getRadius());
   }
​
}

测验类

package com.atguigu.generic;
​
public class TestHasGeneric {
  public static void main(String[] args) {
    CircleComparator com = new CircleComparator();
    System.out.println(com.compare(new Circle(1), new Circle(2)));
​
//      System.out.println(com.compare("圆1", "圆2"));//编译过错,由于"圆1", "圆2"不是Circle类型,是String类型,编译器提前报错,而不是冒着危险在运转时再报错
   }
}

假如有了泛型并运用泛型,那么既能保证安全,又能简化代码。

由于把不安全的因素在编译期间就排除了;已然经过了编译,那么类型一定是符合要求的,就避免了类型转换。

12.1.3 泛型的相关名词

<类型>这种语法方式就叫泛型。

其中:

  • <T>是类型变量(Type Variables),而<T>是代表不知道的数据类型,咱们能够指定为<String>,<Integer>,<Circle>等,那么<类型>的方式咱们成为类型参数;

    • 类比办法的参数的概念,咱们能够把<T>,称为类型形参,将<Circle>称为类型实参,有助于咱们理解泛型;
  • Comparator<T>这种就称为参数化类型(Parameterized Types)。

自从有了泛型之后,Java的数据类型就更丰厚了:

【12】 泛型

Class:Class 类的实例表明正在运转的 Java 应用程序中的类和接口。枚举是一品种,注释是一种接口。每个数组属于被映射为 Class 目标的一个类,一切具有相同元素类型和维数的数组都同享该 Class 目标。根本的 Java 类型(booleanbytecharshortintlongfloatdouble)和关键字 void 也表明为 Class 目标。

  • GenericArrayType:泛化的数组类型,即T[]
  • ParameterizedType:参数化类型,例如:Comparator<T>,Comparator<String>
  • TypeVariable:类型变量,例如:Comparator<T>中的T,Map<K,V>中的K,V
  • WildcardType:通配符类型,例如:Comparator<?>等

12.1.4 在哪里能够声明类型变量<T>

  • 声明类或接口时,在类名或接口名后边声明类型变量,咱们把这样的类或接口称为泛型类或泛型接口
【修饰符】 class 类名<类型变量列表> 【extends 父类】 【implements 父接口们】{
  
}
【修饰符】 interface 接口名<类型变量列表> 【implements 父接口们】{
  
}
​
例如:
public class ArrayList<E>  
public interface Map<K,V>{
   ....
}  
  • 声明办法时,在【修饰符】与回来值类型之间声明类型变量,咱们把声明(是声明不是单纯的运用)了类型变量的办法称为泛型办法
【修饰符】 <类型变量列表> 回来值类型 办法名(【形参列表】)【throws 异常列表】{
  //...
}
​
例如:java.util.Arrays类中的
public static <T> List<T> asList(T... a){
   ....
}

12.2 泛型类与泛型接口

12.2.1 运用中心类库中的泛型类/接口

自从JDK1.5引进泛型的概念之后,对之前中心类库中的API做了很大的修正,例如:调集框架集中的相关接口和类、java.lang.Comparable接口、java.util.Comparator接口、Class类等等。

下面以Collection、ArrayList调集以及Iterator迭代器为例演示,泛型类与泛型接口的运用。

事例一:Collection调集相关类型

(1)创立一个Collection调集(暂时创立ArrayList调集目标),并指定泛型为<Integer>

(2)增加5个[0,100)以内的整数到调集中,

(3)运用foreach遍历输出5个整数,

(4)运用调集的removeIf办法删去偶数,为Predicate接口指定泛型<Ineteger>

(5)再运用Iterator迭代器输出剩余的元素,为Iterator接口指定泛型<Integer>。

package com.atguigu.genericclass.use;
​
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Random;
import java.util.function.Predicate;
​
public class TestNumber {
  public static void main(String[] args) {
    Collection<Integer> coll = new ArrayList<Integer>();
    Random random = new Random();
    for (int i = 1; i <= 5 ; i++) {
      coll.add(random.nextInt(100));
     }
​
    System.out.println("coll中5个随机数是:");
    for (Integer integer : coll) {
      System.out.println(integer);
     }
​
    coll.removeIf(new Predicate<Integer>() {
      @Override
      public boolean test(Integer integer) {
        return integer % 2 == 0;
       }
     });
​
    System.out.println("coll中删去偶数后:");
    Iterator<Integer> iterator = coll.iterator();
    while(iterator.hasNext()){
      Integer number = iterator.next();
      System.out.println(number);
     }
​
   }
}

事例二:Comparable接口

(1)声明矩形类Rectangle,包括特点长和宽,特点私有化,提供有参结构、get/set办法、重写toString办法,提供求面积和周长的办法。

(2)矩形类Rectangle完成java.lang.Comparable<T>接口,并指定泛型为<Rectangle>,重写int compareTo(T t)办法,依照矩形面积比较巨细,面积持平的,依照周长比较巨细。

(3)在测验类中,创立Rectangle数组,并创立5个矩形目标

(4)调用Arrays的sort办法,给矩形数组排序,并显现排序前后的结果。

package com.atguigu.genericclass.use;
​
public class Rectangle implements Comparable<Rectangle>{
  private double length;
  private double width;
​
  public Rectangle(double length, double width) {
    this.length = length;
    this.width = width;
   }
​
  public double getLength() {
    return length;
   }
​
  public void setLength(double length) {
    this.length = length;
   }
​
  public double getWidth() {
    return width;
   }
​
  public void setWidth(double width) {
    this.width = width;
   }
​
  public double area(){
    return length * width;
   }
​
  public double perimeter(){
    return 2 * (length + width);
   }
​
  @Override
  public String toString() {
    return "Rectangle{" +
        "length=" + length +
        ", width=" + width +
        ",area =" + area() +
        ",perimeter = " + perimeter() +
        '}';
   }
​
  @Override
  public int compareTo(Rectangle o) {
    int compare = Double.compare(area(), o.area());
    return compare != 0 ? compare : Double.compare(perimeter(),o.perimeter());
   }
}
​
package com.atguigu.genericclass.use;
​
import java.util.Arrays;
​
public class TestRectangle {
  public static void main(String[] args) {
    Rectangle[] arr = new Rectangle[4];
    arr[0] = new Rectangle(6,2);
    arr[1] = new Rectangle(4,3);
    arr[2] = new Rectangle(12,1);
    arr[3] = new Rectangle(5,4);
​
    System.out.println("排序之前:");
    for (Rectangle rectangle : arr) {
      System.out.println(rectangle);
     }
​
    Arrays.sort(arr);
​
    System.out.println("排序之后:");
    for (Rectangle rectangle : arr) {
      System.out.println(rectangle);
     }
   }
}
​

12.2.2 自界说泛型类与泛型接口

当咱们在类或接口中界说某个成员时,该成员的相关类型是不确定的,而这个类型需求在运用这个类或接口时才干够确定,那么咱们能够运用泛型。

  • 当某个类/接口的非静态实例变量的类型不确定,需求在创立目标或子类承继时才干确定
  • 当某个(些)类/接口的非静态办法的形参类型不确定,需求在创立目标或子类承继时才干确定

语法格局:

【修饰符】 class 类名\<类型变量列表\> 【extends 父类】 【implements 父接口们】{
  
}
【修饰符】 interface 接口名\<类型变量列表\> 【extends 父接口们】{
  
}

留意:

  • <类型变量列表>:能够是一个或多个类型变量,一般都是运用单个的大写字母表明。例如:<T>、<K,V>等。
  • <类型变量列表>中的类型变量不能用于静态成员上。

示例代码:

例如:咱们要声明一个学生类,该学生包括名字、成果,而此刻学生的成果类型不确定,为什么呢,由于,语文老师希望成果是“优秀”、“杰出”、“及格”、“不及格”,数学老师希望成果是89.5, 65.0,英语老师希望成果是’A’,’B’,’C’,’D’,’E’。那么咱们在设计这个学生类时,就能够运用泛型。

package com.atguigu.genericclass.define;
​
public class Student<T>{
  private String name;
  private T score;
​
  public Student() {
    super();
   }
  public Student(String name, T score) {
    super();
    this.name = name;
    this.score = score;
   }
  public String getName() {
    return name;
   }
  public void setName(String name) {
    this.name = name;
   }
  public T getScore() {
    return score;
   }
  public void setScore(T score) {
    this.score = score;
   }
  @Override
  public String toString() {
    return "名字:" + name + ", 成果:" + score;
   }
}

12.2.3 运用泛型类与泛型接口小结

在运用这种参数化的类与接口时,咱们需求指定泛型变量的实践类型参数:

(1)实践类型参数有必要是引证数据类型,不能是根本数据类型

(2)子类承继泛型父类时,子接口承继泛型父接口、或完成类完成泛型父接口时,

  • 指定类型变量对应的实践类型参数,此刻子类或完成类不再是泛型类
package com.atguigu.genericclass.define;
​
//ChineseStudent不再是泛型类
public class ChineseStudent extends Student<String>{
​
  public ChineseStudent() {
    super();
   }
​
  public ChineseStudent(String name, String score) {
    super(name, score);
   }
​
}
public class Rectangle implements Comparable<Rectangle>
  • 指定类型变量(该类型变量能够和本来字母相同,也能够换一个字母),此刻子类、子接口、完成类依然是泛型类或泛型接口
public interface Iterable<T>
public interface Collection<E> extends Iterable<E> //E:Element元素
public interface List<E>extends Collection<E>
public class ArrayList<E>extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, Serializable

(3)在创立泛型类的目标时指定类型变量对应的实践类型参数

package com.atguigu.genericclass.define;
​
public class TestStudent {
  public static void main(String[] args) {
    //语文老师运用时:
    Student<String> stu1 = new Student<String>("张三", "杰出");
    ChineseStudent chineseStudent = new ChineseStudent("张三", "杰出");
​
    //数学老师运用时:
    //Student<double> stu2 = new Student<double>("张三", 90.5);//过错,有必要是引证数据类型
    Student<Double> stu2 = new Student<Double>("张三", 90.5);
​
    //英语老师运用时:
    Student<Character> stu3 = new Student<Character>("张三", 'C');
​
    //过错的指定
    //Student<Object> stu = new Student<String>();//过错的
   }
}
​

JDK1.7支持简写方式:Student<String> stu1 = new Student<>(“张三”, “杰出”);

指定泛型实参时,有必要左右两头一致,不存在多态现象

12.3 泛型办法

12.3.1 泛型办法的调用

在java.util.Arrays数组东西类中,有很多泛型办法,例如:

  • public static <T> List<T> asList(T… a):将实参目标顺次增加到一个固定巨细的List列表调集中。
  • public static <T> T[] copyOf(T[] original, int newLength):仿制恣意目标数组,新数组长度为newLength。
package com.atguigu.genericmethod;
import java.util.Arrays;
import java.util.List;
public class TestArrays {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("java", "world", "hello", "atguigu");
        System.out.println(list);
        String[] arr = {"java", "world", "hello"};
        String[] strings = Arrays.copyOf(arr, arr.length * 2);
        System.out.println(Arrays.toString(strings));
    }
}

泛型办法在调用时,由实参的类型确定泛型办法类型变量的详细类型。

12.3.2 自界说泛型办法

前面介绍了在界说类、接口时能够声明<类型变量>,在该类的办法和特点界说、接口的办法界说中,这些<类型变量>可被当成一般类型来用。可是,在别的一些情况下,

(1)假如咱们界说类、接口时没有运用<类型变量>,可是某个办法形参类型不确定时,这个办法能够单独界说<类型变量>;

(2)别的咱们之前说类和接口上的类型形参是不能用于静态办法中,那么当某个静态办法的形参类型不确定时,静态办法能够单独界说<类型变量>。

语法格局:

【修饰符】 <类型变量列表> 回来值类型 办法名(【形参列表】)【throws 异常列表】{
  //...
}
  • <类型变量列表>:能够是一个或多个类型变量,一般都是运用单个的大写字母表明。例如:<T>、<K,V>等。

示例代码:

咱们编写一个数组东西类,包括能够给恣意目标数组进行从小到大排序,调用元素目标的compareTo办法比较元素的巨细联系。

package com.atguigu.genericmethod;
​
public class MyArrays {
  public static <T> void sort(T[] arr){
    for (int i = 1; i < arr.length; i++) {
      for (int j = 0; j < arr.length-i; j++) {
        if(((Comparable<T>)arr[j]).compareTo(arr[j+1])>0){
          T temp = arr[j];
          arr[j] = arr[j+1];
          arr[j+1] = temp;
         }
       }
     }
   }
}
package com.atguigu.genericmethod;
​
import com.atguigu.generic.Circle;
​
import java.util.Arrays;
​
public class MyArraysTest {
  public static void main(String[] args) {
    int[] arr = {3,2,5,1,4};
//      MyArrays.sort(arr);//过错的,由于int[]不是目标数组
​
    String[] strings = {"hello","java","chai"};
    MyArrays.sort(strings);
    System.out.println(Arrays.toString(strings));
​
    Circle[] circles = {new Circle(2.0),new Circle(1.2),new Circle(3.0)};
    MyArrays.sort(circles); //编译经过,运转报错,Circle没有完成Comparable接口
   }
}

12.4 类型变量的上限与泛型的擦除

12.4.1 类型变量的上限

当在声明类型变量时,假如不希望这个类型变量代表恣意引证数据类型,而是某个系列的引证数据类型,那么能够设定类型变量的上限。

语法格局:

<类型变量  extends 上限>

假如有多个上限

<类型变量  extends 上限1 & 上限2>

假如多个上限中有类有接口,那么只能有一个类,而且有必要写在最左面。接口的话,能够多个。

假如在声明<类型变量>时没有指定任何上限,默认上限是java.lang.Object。

1、界说泛型类的类型变量时指定上限

例如:咱们要声明一个两个数算术运算的东西类,要求两个数有必要是Number数字类型,而且完成Comparable接口。

package com.atguigu.limmit;
​
import java.math.BigDecimal;
import java.math.BigInteger;
​
public class NumberTools<T extends Number & Comparable<T>>{
  private T a;
  private T b;
​
  public NumberTools(T a, T b) {
    super();
    this.a = a;
    this.b = b;
   }
​
  public T getSum(){
    if(a instanceof BigInteger){
      return (T) ((BigInteger) a).add((BigInteger)b);
     }else if(a instanceof BigDecimal){
      return (T) ((BigDecimal) a).add((BigDecimal)b);
     }else if(a instanceof Byte){
      return (T)(Byte.valueOf((byte)((Byte)a+(Byte)b)));
     }else if(a instanceof Short){
      return (T)(Short.valueOf((short)((Short)a+(Short)b)));
     }else if(a instanceof Integer){
      return (T)(Integer.valueOf((Integer)a+(Integer)b));
     }else if(a instanceof Long){
      return (T)(Long.valueOf((Long)a+(Long)b));
     }else if(a instanceof Float){
      return (T)(Float.valueOf((Float)a+(Float)b));
     }else if(a instanceof Double){
      return (T)(Double.valueOf((Double)a+(Double)b));
     }
    throw new UnsupportedOperationException("不支持该操作");
   }
​
  public T getSubtract(){
    if(a instanceof BigInteger){
      return (T) ((BigInteger) a).subtract((BigInteger)b);
     }else if(a instanceof BigDecimal){
      return (T) ((BigDecimal) a).subtract((BigDecimal)b);
     }else if(a instanceof Byte){
      return (T)(Byte.valueOf((byte)((Byte)a-(Byte)b)));
     }else if(a instanceof Short){
      return (T)(Short.valueOf((short)((Short)a-(Short)b)));
     }else if(a instanceof Integer){
      return (T)(Integer.valueOf((Integer)a-(Integer)b));
     }else if(a instanceof Long){
      return (T)(Long.valueOf((Long)a-(Long)b));
     }else if(a instanceof Float){
      return (T)(Float.valueOf((Float)a-(Float)b));
     }else if(a instanceof Double){
      return (T)(Double.valueOf((Double)a-(Double)b));
     }
    throw new UnsupportedOperationException("不支持该操作");
   }
}

测验类

package com.atguigu.limmit;
​
public class NumberToolsTest {
  public static void main(String[] args) {
    NumberTools<Integer> tools = new NumberTools<Integer>(8,5);
    Integer sum = tools.getSum();
    System.out.println("sum = " + sum);
    Integer subtract = tools.getSubtract();
    System.out.println("subtract = " + subtract);
   }
}

2、界说泛型办法的类型变量时指定上限

咱们编写一个数组东西类,包括能够给恣意目标数组进行从小到大排序,调用元素目标的compareTo办法比较元素的巨细联系。要求数组的元素类型有必要是java.lang.Comparable<T>接口类型。

package com.atguigu.limmit;
public class MyArrays {
    public static <T extends Comparable<T>> void sort(T[] arr){
        for (int i = 1; i < arr.length; i++) {
            for (int j = 0; j < arr.length-i; j++) {
                if(arr[j].compareTo(arr[j+1])>0){
                    T temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
    }
}

测验类

package com.atguigu.limmit;
​
import com.atguigu.generic.Circle;
​
import java.util.Arrays;
​
public class MyArraysTest {
  public static void main(String[] args) {
    int[] arr = {3,2,5,1,4};
//      MyArrays.sort(arr);//过错的,由于int[]不是目标数组
​
    String[] strings = {"hello","java","chai"};
    MyArrays.sort(strings);
    System.out.println(Arrays.toString(strings));
​
    Circle[] circles = {new Circle(2.0),new Circle(1.2),new Circle(3.0)};
//     MyArrays.sort(circles); //编译报错
   }
}

12.4.2 泛型擦除

当运用参数化类型的类或接口时,假如没有指定泛型,那么会怎么样呢?

会产生泛型擦除,主动依照最左面的第一个上限处理。假如没有指定上限,上限即为Object。

package com.atguigu.limmit;
​
import java.util.ArrayList;
import java.util.Collection;
​
public class TestErase {
  public static void main(String[] args) {
    NumberTools tools = new NumberTools(8,5);
    Number sum = tools.getSum();//主动依照Number处理
    System.out.println("sum = " + sum);
    Number subtract = tools.getSubtract();
    System.out.println("subtract = " + subtract);
​
    Collection coll = new ArrayList();
    coll.add("hello");
    coll.add(1);
    for (Object o : coll) {//主动依照Object处理
      System.out.println(o);
     }
   }
}
​

12.5 类型通配符

12.5.1 Java泛型指定限制问题

声明一个办法,形参是Collection,可是元素类型不确定,怎么办?

package com.atguigu.wildcard;
​
import java.util.ArrayList;
import java.util.Collection;
​
public class TestProblem {
  public static void m1(Collection<Object> coll){
    for (Object o : coll) {
      System.out.println(o);
     }
   }
  public static void m2(Collection coll){
    for (Object o : coll) {
      System.out.println(o);
     }
   }
  public static <T> void m3(Collection<T> coll){
    for (T o : coll) {
      System.out.println(o);
     }
   }
  
  public static void main(String[] args) {
    m1(new ArrayList<Object>());//Collection<Object> coll = new ArrayList<Object>();
    m1(new ArrayList<>());//Collection<Object> coll = new ArrayList<>();//与上面彻底等价
    m1(new ArrayList());//Collection<Object> coll = new ArrayList();//有正告
//     m1(new ArrayList<String>());//Collection<Object> coll = new ArrayList<String>();//过错//编译看左面,左面泛型擦除,此处泛型依照Object处理,右边泛型指定啥都没用
    m2(new ArrayList<Object>());//Collection coll = new ArrayList<Object>();
    m2(new ArrayList<>());//Collection coll = new ArrayList<>();//与上面彻底等价
    m2(new ArrayList());//Collection coll = new ArrayList();//泛型擦除
    m2(new ArrayList<String>());//Collection coll = new ArrayList<String>();m3(new ArrayList<Object>());//Collection<Object> coll = new ArrayList<Object>();
    m3(new ArrayList<>());//Collection<> coll = new ArrayList<>();//与上面彻底等价
    m3(new ArrayList());//Collection<Object> coll = new ArrayList();//有正告
    m3(new ArrayList<String>());//Collection<String> coll = new ArrayList<String>();
   }
}

12.5.2 类型通配符

当咱们声明一个变量/形参时,这个变量/形参的类型是一个泛型类或泛型接口,例如:Comparator<T>类型,可是咱们依然无法确定这个泛型类或泛型接口的类型变量<T>的详细类型,此刻咱们考虑运用类型通配符 ? 。

package com.atguigu.wildcard;
​
import java.util.ArrayList;
import java.util.Collection;
​
public class TestWildcard {
  public static void m4(Collection<?> coll){
    for (Object o : coll) {
      System.out.println(o);
     }
   }
​
  public static void main(String[] args) {
    //右边泛型指定为恣意类型或不指定都能够
    m4(new ArrayList<Object>());//Collection<?> coll = new ArrayList<Object>();
    m4(new ArrayList<>());//Collection<?> coll = new ArrayList<>();
    m4(new ArrayList());//Collection<?> coll = new ArrayList();
    m4(new ArrayList<String>());//Collection<?> coll = new ArrayList<String>();
   }
}

12.5.3 类型通配符的三种运用方式

类型通配符 ? 有三种运用方式:

  • :完好方式为:类名 或接口名,此刻?代表恣意类型。
  • :完好方式为:类名 或接口名,此刻?代表上限类型自身或许上限的子类,即?代表 <= 上限的类型。
  • :完好方式为:类名 或接口名,此刻?代表下限类型自身或许下限的父类,即?代表>= 下限的类型。

事例:

声明一个调集东西类MyCollections,要求包括:

  • public static boolean different(Collection c1, Collection c2):比较两个Collection调集,此刻两个Collection调集的泛型能够是恣意类型,假如两个调集中没有相同的元素,则回来true,否则回来false。
  • public static void addAll(Collection<? super T> c1, T… args):能够将恣意类型的多个目标增加到一个Collection调集中,此刻要求Collection调集的泛型指定有必要>=元素类型。
  • public static void copy(Collection<? super T> dest,Collection<? extends T> src):能够将一个Collection调集的元素仿制到另一个Collection调集中,此刻要求原Collection泛型的类型<=目标Collection的泛型类型。
package com.atguigu.wildcard;
import java.util.Collection;
public class MyCollections {
    public static boolean different(Collection<?> c1, Collection<?> c2){
        return c1.containsAll(c2) && c2.containsAll(c1);
    }
    public static <T> void addAll(Collection<? super T> c1, T... args){
        for (int i = 0; i < args.length; i++) {
            c1.add(args[i]);
        }
    }
    public static <T> void copy(Collection<? super T> dest,Collection<? extends T> src){
        for (T t : src) {
            dest.add(t);
        }
    }
}

测验类

package com.atguigu.wildcard;
​
import java.util.ArrayList;
import java.util.Collection;
​
public class MyCollectionsTest {
  public static void main(String[] args) {
    Collection<Integer> c1 = new ArrayList<Integer>();
    MyCollections.addAll(c1,1,2,3,4,5);
    System.out.println("c1 = " + c1);
​
    Collection<String> c2 = new ArrayList<String>();
    MyCollections.addAll(c2,"hello","java","world");
    System.out.println("c2 = " + c2);
​
    System.out.println("c1 != c2 " + MyCollections.different(c1, c2));
​
    Collection<Object> c3 = new ArrayList<>();
    MyCollections.copy(c3,c1);
    MyCollections.copy(c3,c2);
    System.out.println("c3 = " + c3);
   }
}
​

12.5.4 运用类型通配符来指定类型参数的问题

(1)假如把”泛型类”指定为”泛型类<?>”:那么该泛型类中一切参数是T类型的办法或成员都无法正常运用。参数类型不是T类型的办法照旧运用。

import org.junit.Test;
​
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
​
public class TestProblem {
  @Test
  public void test01(){
    Collection<?> coll = new ArrayList<>();
//     coll.add("hello");
//     coll.add(1);
//     coll.add(1.0);
    /*
    上面一切增加操作都报错。
    为什么?
    由于<?>表明不知道的类型,调集的元素是不确定的,那么增加恣意类型目标都有危险。
    
    void add(E t)办法无法正常运用
    由于此刻E由?表明,即表明直到add办法被调用时,E的类型依然不确定,所以该办法无法正常运用
     */
​
    Collection<?> coll2 = Arrays.asList("hello","java","world");
    for (Object o : coll2) {
      System.out.println(o);
     }
   }
}

(2)假如把”泛型类”指定为”泛型类<? extends 上限>”:那么该泛型类中一切参数是T类型的办法或成员都无法正常运用。参数类型不是T类型的办法照旧运用。

import org.junit.Test;
​
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
​
public class TestProblem {
    @Test
  public void test02(){
    Collection<? extends Number> coll = new ArrayList<Double>();
//     coll.add(1);
//     coll.add(1.0);
//     coll.add("hello");
    /*
    上面一切增加操作都报错。
    为什么?
    由于<?>表明不知道的类型,代表<=Number的恣意一种
    
    void add(E t)办法无法正常运用
    由于此刻<E>由<? extends Number>表明,即表明直到add办法被调用时,E的类型依然不确定,所以该办法无法正常运用。它能够是<=Number的恣意一品种型。
     */
   }
}    

(3)假如把”泛型类”指定为”泛型类<? super 下限>”:那么该泛型类中一切参数是T类型的办法或成员都能够运用,可是有要求。参数类型不是T类型的办法照旧运用。

import org.junit.Test;
​
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
​
public class TestProblem {
  @Test
  public void test03(){
    Collection<? super Number> coll = new ArrayList<>();
    coll.add(1);
    coll.add(1.0);
//     coll.add("hello");
    /*
    前两个能够,最后一个不行
    <? super Number>代表>=Number类型。最小可能是Number。
    //能够增加Number目标或Number子类目标
     */
   }
}