Java泛型介绍

Java泛型简介

Posted by qin4zhang on December 28, 2019

注意

想法及时记录,实现可以待做。

Java泛型

主要对泛型界限做介绍,

extends

这样的通配符声明List<? extends Number> foo3表示都是合法的分配:

List<? extends Number> foo3 = new ArrayList<Number>();  // Number "extends" Number (in this context)
List<? extends Number> foo3 = new ArrayList<Integer>(); // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>();  // Double extends Number
  • 读取

    给定以上可能的分配,可以保证从List foo3中读取什么类型的对象

    • 你可以读取Number,因为可以分配给foo3的任何列表都包含NumberNumber的子类。
    • 你不可读取Integer,因为foo3可能指向List<Double>
    • 你不可读取Double,因为foo3可能指向List<Integer>
  • 写入

    给定以上可能的分配,你可以将什么类型的对象添加到List foo3,这对于所有以上可能的ArrayList分配都是合法的:

    • 你不可以添加Integer,因为foo3可能会指向List<Double>
    • 你不可以添加Double,因为foo3可能会指向List<Integer>
    • 你不可以添加Number,因为foo3可能会指向List<Integer>

你不能将任何对象添加到List<? extends T>,因为你不能保证它真正指向的是哪种List,因此不能保证该List中允许该对象。 唯一能”保证”的是你只能从中读取内容,并且会获得TT的子类。

super

现在考虑下 List <? super T>这种情形。

List<? super Integer> foo3表示以下任何一项都是合法的分配:

List<? super Integer> foo3 = new ArrayList<Integer>();  // Integer is a "superclass" of Integer (in this context)
List<? super Integer> foo3 = new ArrayList<Number>();   // Number is a superclass of Integer
List<? super Integer> foo3 = new ArrayList<Object>();   // Object is a superclass of Integer
  • 读取

    给定以上可能的分配,可以保证从List foo3中读取什么类型的对象

    • 你不能保证使用Integer,因为foo3可能指向List<Number>List<Object>
    • 你不能保证使用Number,因为foo3可能指向List<Object>
    • 唯一的保证是你将获得一个Object的实例或Object的子类(但不知道是什么子类)
  • 写入

    给定以上可能的分配,你可以将什么类型的对象添加到List foo3,这对于所有以上可能的ArrayList分配都是合法的:

    • 你可以添加一个Integer,因为上述任何列表中都允许使用Integer
    • 你可以添加Integer子类的实例,因为上述任何列表中都允许使用Integer子类的实例
    • 你不能添加Double,因为foo3可能指向ArrayList<Integer>
    • 你不能添加Number,因为foo3可能指向ArrayList<Integer>
    • 你不能添加Object,因为foo3可能指向ArrayList<Integer>

你无法从List<? super T>中读取特定的T类型(例如Number),因为你不能保证它真正指向的是哪种列表。 你能唯一保证的是你能够添加类型T(或T的任何子类)的值,而不会破坏所指向列表的完整性。

PECS

记住PECS:”Producer Extends, Consumer Super”。

  • “Producer Extends”

    如果你需要一个List来生产T值(要从列表中读取T),则需要使用? extends T,例如List<? extends Integer>。但是你不能添加元素到此列表中。

  • “Consumer Super”

    如果你需要使用List来消费T值(要将T写入列表中),则需要使用? super T,例如List<? super Integer>。但是并不能保证你可以从该列表中读取具体类型的对象。

  • 如果你既需要读取列表也要写入列表,则需要完全不使用通配符对其进行声明,例如List<Integer>

示例

请注意,源列表src(生产列表)如何使用extends,而目标列表dest(消费列表)如何使用super

public class Collections { 
  public static <T> void copy(List<? super T> dest, List<? extends T> src) {
      for (int i = 0; i < src.size(); i++) 
        dest.set(i, src.get(i)); 
  } 
}
// copy(dest, src)
Collections.copy(new ArrayList<Number>(), new ArrayList<Number>());
Collections.copy(new ArrayList<Number>(), new ArrayList<Integer>());
Collections.copy(new ArrayList<Object>(), new ArrayList<Number>());
Collections.copy(new ArrayList<Object>(), new ArrayList<Double>());

参考

  1. difference-between-super-t-and-extends-t-in-java
  2. Java Generics FAQs - Type Arguments