1 Star 0 Fork 4

jintop1/java-generics-tutorial

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

Java泛型教程

泛型类(generic class)

泛型类(generic class)

泛型类声明:在普通类声明后跟一个类型参数(type parameter)。

泛型类的类型参数部分可以有一个或多个用逗号分隔的类型参数。 这些类称为参数化类(parameterized classes)或参数化类型(parameterized types),因为它们接受一个或多个参数。

语法

public class Box<T> {
   private T t;
}

其中:

  • Box − Box 是一个泛型类
  • T − 传递给泛型类的一个泛型参数. 它可以是.
  • t − 泛型T的实例。

T是传递给泛型类Box的类型参数,应在创建Box对象时传递。

例子

com.theten52.tutarial.generics.a0.GenericsTester

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> integerBox = new Box<Integer>();
      Box<String> stringBox = new Box<String>();

      integerBox.add(new Integer(10));
      stringBox.add(new String("Hello World"));

      System.out.printf("Integer Value :%d\n", integerBox.get());
      System.out.printf("String Value :%s\n", stringBox.get());
   }
}

class Box<T> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }   
}

输出

Integer Value :10
String Value :Hello World

类型参数(type parameter)的命名约定

按照惯例,类型参数名称被命名为单个大写字母,因此可以轻松区分一个名称是类型参数还是普通的类或接口。 以下是常用类型参数名称列表:

  • E - Element,主要由Java集合框架使用。
  • K - Key,主要用于表示Map中键的参数类型。
  • V - Value,主要用于表示Map中值的参数类型。
  • N - Number,主要用于表示数字。
  • T - Type,主要用于表示第一个泛型类型参数。
  • S - Type,主要用于表示第二个泛型类型参数。
  • U - Type,主要用于表示第三个泛型类型参数。
  • V - Type,主要用于表示第四个泛型类型参数。

例子

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer, String> box = new Box<Integer, String>();
      box.add(Integer.valueOf(10),"Hello World");
      System.out.printf("Integer Value :%d\n", box.getFirst());
      System.out.printf("String Value :%s\n", box.getSecond());

      Pair<String, Integer> pair = new Pair<String, Integer>(); 
      pair.addKeyValue("1", Integer.valueOf(10));
      System.out.printf("(Pair)Integer Value :%d\n", pair.getValue("1"));

      CustomList<Box> list = new CustomList<Box>();
      list.addItem(box);
      System.out.printf("(CustomList)Integer Value :%d\n", list.getItem(0).getFirst());
   }
}

class Box<T, S> {
   private T t;
   private S s;

   public void add(T t, S s) {
      this.t = t;
      this.s = s;
   }

   public T getFirst() {
      return t;
   } 

   public S getSecond() {
      return s;
   } 
}

class Pair<K,V>{
   private Map<K,V> map = new HashMap<K,V>();

   public void addKeyValue(K key, V value) {
      map.put(key, value);
   }

   public V getValue(K key) {
      return map.get(key);
   }
}

class CustomList<E>{
   private List<E> list = new ArrayList<E>();

   public void addItem(E value) {
      list.add(value);
   }

   public E getItem(int index) {
      return list.get(index);
   }
}

输出

Integer Value :10
String Value :Hello World
(Pair)Integer Value :10
(CustomList)Integer Value :10

类型推断(Type Inference)

类型推断表示Java编译器有能力去查看方法调用及其相应的声明,以检查和确定类型参数。 类型推断算法检查参数的类型,如果可用,则返回分配的类型。 类型推断算法尝试查找可以填充所有类型参数的特定类型。

如果不使用类型推断,则编译器会生成未经检查的转换警告。

语法

Box<Integer> integerBox = new Box<>();

其中:

  • Box − Box是一个泛型类。
  • <> − 菱形运算符表示类型推断。

使用菱形运算符,编译器确定参数的类型。 从Java SE 7版开始,此运算符可以使用。

例子

public class GenericsTester {
   public static void main(String[] args) {
      //类型推断   
      Box<Integer> integerBox = new Box<>();
      Box<String> stringBox = new Box<String>();

      integerBox.add(new Integer(10));
      stringBox.add(new String("Hello World"));

      System.out.printf("Integer Value :%d\n", integerBox.get());
      System.out.printf("String Value :%s\n", stringBox.get());
   }
}

class Box<T> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }   
}

泛型方法(generic method)

您可以编写一个可以使用不同类型的参数调用的泛型方法声明。 根据传递给泛型方法的参数类型,编译器会适当地处理每个方法调用。 以下是定义泛型方法的规则:

  • 所有泛型方法声明都有一个由尖括号(“<”和“>”)分隔的类型参数部分,它位于方法的返回类型之前。
  • 每个类型参数部分包含一个或多个以逗号分隔的类型参数。 类型参数(也称为类型变量【type variable】)是指定泛型类型名称的标识符。
  • 类型参数可用于声明返回类型,并充当传递给泛型方法的参数类型的占位符,这些参数称为实际类型参数(actual type arguments)
  • 泛型方法的方法体声明为任何其他方法的方法体。 请注意,类型参数只能表示引用类型,而不能表示基本类型(如int,double和char)。

例子

public class GenericMethodTest {
   // 泛型方法 printArray
   public static <E> void printArray( E[] inputArray ) {
      // 打印数组元素
      for(E element : inputArray) {
         System.out.printf("%s ", element);
      }
      System.out.println();
   }

   public static void main(String args[]) {
      // 创建 Integer, Double and Character 数组
      Integer[] intArray = { 1, 2, 3, 4, 5 };
      Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
      Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };

      System.out.println("Array integerArray contains:");
      printArray(intArray);   // 传入一个 Integer 数组

      System.out.println("\nArray doubleArray contains:");
      printArray(doubleArray);   // 传入一个 Double 数组

      System.out.println("\nArray characterArray contains:");
      printArray(charArray);   // 传入一个 Character 数组
   }
}

输出

Array integerArray contains:
1 2 3 4 5 

Array doubleArray contains:
1.1 2.2 3.3 4.4 

Array characterArray contains:
H E L L O

多类型参数(Multiple Type Parameters)

泛型类可以具有多个类型参数。以下示例将展示上述概念。

例子

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer, String> box = new Box<Integer, String>();
      box.add(Integer.valueOf(10),"Hello World");
      System.out.printf("Integer Value :%d\n", box.getFirst());
      System.out.printf("String Value :%s\n", box.getSecond());

      Box<String, String> box1 = new Box<String, String>();
      box1.add("Message","Hello World");
      System.out.printf("String Value :%s\n", box1.getFirst());
      System.out.printf("String Value :%s\n", box1.getSecond());
   }
}

class Box<T, S> {
   private T t;
   private S s;

   public void add(T t, S s) {
      this.t = t;
      this.s = s;
   }

   public T getFirst() {
      return t;
   } 

   public S getSecond() {
      return s;
   } 
}

输出

Integer Value :10
String Value :Hello World
String Value :Message
String Value :Hello World

参数化类型(Parameterized Types)

泛型类可以具有参数化类型,其中类型参数可以替换为参数化类型。以下示例将展示上述概念。

例子

import java.util.ArrayList;
import java.util.List;


public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer, List<String>> box = new Box<Integer, List<String>>();
      
      List<String> messages = new ArrayList<String>();
      
      messages.add("Hi");
      messages.add("Hello");
      messages.add("Bye");      
      
      box.add(Integer.valueOf(10),messages);
      System.out.printf("Integer Value :%d\n", box.getFirst());
      System.out.printf("String Value :%s\n", box.getSecond());

      
   }
}

class Box<T, S> {
   private T t;
   private S s;

   public void add(T t, S s) {
      this.t = t;
      this.s = s;
   }

   public T getFirst() {
      return t;
   } 

   public S getSecond() {
      return s;
   } 
}

输出

Integer Value :10
String Value :[Hi, Hello, Bye]

原始类型(Raw Types)

原始类型:一个泛型类虽然定义了类型参数,但是使用时并未给其赋值。此时我们称此类型成为原始类型。

如果原始类型的类型参数在创建过程中未传递,则原始类型是通用类或接口的对象。以下示例将展示上述概念。

例子

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> box = new Box<Integer>();
      
      box.set(Integer.valueOf(10));
      System.out.printf("Integer Value :%d\n", box.getData());
      
      
      Box rawBox = new Box();
      
      //不会有编译警告
      rawBox = box;
      System.out.printf("Integer Value :%d\n", rawBox.getData());
      
      //set(T) 方法会有一个 Unchecked call 的 Warning
      rawBox.set(Integer.valueOf(10));
      System.out.printf("Integer Value :%d\n", rawBox.getData());
      
      //此行代码会有一个 Unchecked assignment 的 Warning
      box = rawBox;
      System.out.printf("Integer Value :%d\n", box.getData());
   }
}

class Box<T> {
   private T t; 

   public void set(T t) {
      this.t = t;
   }

   public T getData() {
      return t;
   } 
}

输出

Integer Value :10
Integer Value :10
Integer Value :10
Integer Value :10

有界类型(Bounded Type)

有界类型参数(Bounded Type Parameters)

有时您可能想限制允许传递给类型参数的类型的种类。例如,对Number进行操作的方法可能只希望接受Number或其子类的实例。这就是有界类型参数的用途。

要声明一个有界的类型参数,请列出类型参数的名称,然后列出extends关键字,然后列出其上限。

例子

public class MaximumTest {
   // 确定三个可比较对象中的最大对象
   public static <T extends Comparable<T>> T maximum(T x, T y, T z) {
      T max = x;   // assume x is initially the largest
      
      if(y.compareTo(max) > 0) {
         max = y;   // y is the largest so far
      }
      
      if(z.compareTo(max) > 0) {
         max = z;   // z is the largest now                 
      }
      return max;   // returns the largest object   
   }
   
   public static void main(String args[]) {
      System.out.printf("Max of %d, %d and %d is %d\n\n", 
         3, 4, 5, maximum( 3, 4, 5 ));

      System.out.printf("Max of %.1f,%.1f and %.1f is %.1f\n\n",
         6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ));

      System.out.printf("Max of %s, %s and %s is %s\n","pear",
         "apple", "orange", maximum("pear", "apple", "orange"));
   }
}

输出

Max of 3, 4 and 5 is 5

Max of 6.6,8.8 and 7.7 is 8.8

Max of pear, apple and orange is pear

多有界类型参数(Multiple Bounds)

类型参数可以具有多个边界。

语法

public static <T extends Number & Comparable<T>> T maximum(T x, T y, T z)

其中

  • maximum-maximum是一种通用方法。
  • **T-**传递给泛型方法的泛型类型参数。它可以带任何对象。

T是传递给泛型类Box的类型参数,并且应该是Number类的子类型,并且必须包含Comparable接口。如果将一个类作为有界传递,则应在接口之前首先传递它,否则会发生编译时错误(先声明类,再在&后声明接口)。

例子

public class GenericsTester {
   public static void main(String[] args) {
      System.out.printf("Max of %d, %d and %d is %d\n\n", 
         3, 4, 5, maximum( 3, 4, 5 ));

      System.out.printf("Max of %.1f,%.1f and %.1f is %.1f\n\n",
         6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ));
   }

   public static <T extends Number & Comparable<T>> T maximum(T x, T y, T z) {
      T max = x;      
      if(y.compareTo(max) > 0) {
         max = y;   
      }

      if(z.compareTo(max) > 0) {
         max = z;                    
      }
      return max;      
   }

   // 一下声明会导致编译错误
   /* public static <T extends Comparable<T> & Number> T maximum1(T x, T y, T z) {
      T max = x;      
      if(y.compareTo(max) > 0) {
         max = y;   
      }

      if(z.compareTo(max) > 0) {
         max = z;                    
      }
      return max;   
   }*/
}

输出

Max of 3, 4 and 5 is 5

Max of 6.6,8.8 and 7.7 is 8.8

对集合的支持(list,set,map)

语法

List<T> list = new ArrayList<T>();
Set<T> set = new HashSet<T>();
Set<T> set = new HashSet<T>();

通配符(Wild Cards)

上界通配符(Upper Bounded Wildcards)

问号(?)代表通配符,代表泛型中的未知类型。有时您可能想限制允许传递给类型参数的类型的种类。例如,对Number进行操作的方法可能只希望接受Number或其子类的实例。

要声明一个上界通配符参数,先声明“?”,其次是extends关键字,最后是其上限。

import java.util.Arrays;
import java.util.List;

public class GenericsTester {

   public static double sum(List<? extends Number> numberlist) {
      double sum = 0.0;
      for (Number n : numberlist) sum += n.doubleValue();
      return sum;
   }

   public static void main(String args[]) {
      List<Integer> integerList = Arrays.asList(1, 2, 3);
      System.out.println("sum = " + sum(integerList));

      List<Double> doubleList = Arrays.asList(1.2, 2.3, 3.5);
      System.out.println("sum = " + sum(doubleList));
   }
}

输出

sum = 6.0
sum = 7.0

无界通配符(Unbounded Wildcards)

问号(?)代表通配符,代表泛型中的未知类型。有时可以使用Object类中提供的功能来实现方法,或者代码独立于类型参数时,可以使用任何对象。

例子

public class GenericsTester {
   public static void printAll(List<?> list) {
      for (Object item : list)
         System.out.println(item + " ");
   }

   public static void main(String args[]) {
      List<Integer> integerList = Arrays.asList(1, 2, 3);
      printAll(integerList);
      List<Double> doubleList = Arrays.asList(1.2, 2.3, 3.5);
      printAll(doubleList);
   }
}

输出

1 
2 
3 
1.2 
2.3 
3.5 

下界通配符(Lower Bounded Wildcards)

问号(?)代表通配符,代表泛型中的未知类型。有时您可能想限制允许传递给类型参数的类型的种类。例如,对数字进行操作的方法可能只希望接受Integer或其超类(例如Number)的实例。

要声明下界通配符参数,先声明?,后跟super关键字,然后是其下界。

例子

import java.util.ArrayList;
import java.util.List;

public class GenericsTester {

   public static void addCat(List<? super Cat> catList) {
      catList.add(new RedCat());
      System.out.println("Cat Added");
   }

   public static void main(String[] args) {
      List<Animal> animalList= new ArrayList<Animal>();
      List<Cat> catList= new ArrayList<Cat>();
      List<RedCat> redCatList= new ArrayList<RedCat>();
      List<Dog> dogList= new ArrayList<Dog>();

      //add list of super class Animal of Cat class
      addCat(animalList);

      //add list of Cat class
      addCat(catList);

      //编译错误,不能添加Cat的子类RedCat到list中
      //addCat(redCatList);

      //编译错误,不能添加Animal的子类Dog到Cat类的list中
      //addCat.addMethod(dogList); 
   }
}
class Animal {}

class Cat extends Animal {}

class RedCat extends Cat {}

class Dog extends Animal {}

输出

Cat Added
Cat Added

通配符使用准则

通配符有三种方式可以使用:

  • 上界通配符 − ? extends Type.
  • 下界通配符 − ? super Type.
  • 无界通配符 − ?

为了确定最适合某些条件下的通配符类型,我们首先将传递给方法的参数类型分类为in变量out变量

  • in变量 − in变量将数据提供给代码。例如,copy(src, dest)。此处src充当要复制的数据的来源。
  • out变量 −out变量保存由代码更新的数据。例如,copy(src, dest)。在此,dest充当要复制数据的目的地。

通配符准则

  • 上界通配符-如果变量属于in变量,则将extend关键字和通配符一起使用。
  • 下界通配符-如果变量属于out变量,则将super关键字与通配符一起使用。
  • 无界通配符-如果可以使用Object类方法访问变量,请使用无界通配符
  • 没有通配符-如果代码同时访问in变量out变量中的数据,则不要使用通配符。

例子

import java.util.ArrayList;
import java.util.List;

public class GenericsTester {

   //in变量:上界通配符
   public static void deleteCat(List<? extends Cat> catList, Cat cat) {
      catList.remove(cat);
      System.out.println("Cat Removed");
   }

   //out变量:下界通配符
   public static void addCat(List<? super RedCat> catList) {
      catList.add(new RedCat("Red Cat"));
      System.out.println("Cat Added");
   }

   //无界通配符
   //使用 Object 的 toString() 方法
   public static void printAll(List<?> list) {
      for (Object item : list)
         System.out.println(item + " ");
   }

   public static void main(String[] args) {

      List<Animal> animalList= new ArrayList<Animal>();
      List<RedCat> redCatList= new ArrayList<RedCat>();

      //add list of super class Animal of Cat class
      addCat(animalList);
      //add list of Cat class
      addCat(redCatList);  
      addCat(redCatList);  

      //print all animals
      printAll(animalList);
      printAll(redCatList);

      Cat cat = redCatList.get(0);
      //delete cat
      deleteCat(redCatList, cat);
      printAll(redCatList); 
   }
}

class Animal {
   String name;
   Animal(String name) { 
      this.name = name;
   }
   public String toString() { 
      return name;
   }
}

class Cat extends Animal { 
   Cat(String name) {
      super(name);
   }
}

class RedCat extends Cat {
   RedCat(String name) {
      super(name);
   }
}

class Dog extends Animal {
   Dog(String name) {
      super(name);
   }
}

输出

Cat Added
Cat Added
Cat Added
Red Cat 
Red Cat 
Red Cat 
Cat Removed
Red Cat 

类型擦除(Type Erasure)

类型擦除(Type Erasure)

泛型用于在编译时进行更严格的类型检查,并提供泛型编程。为了实现泛型行为,java编译器应用了类型擦除。类型擦除是编译器用**实际的类(actual class )桥接方法(bridge method)**替换泛型参数的过程。在类型擦除中,编译器确保不创建额外的类,并且没有运行时开销。

类型擦除规则

  • 如果使用了有界类型参数,则用其界限类(extend 或 super 关键字后跟的类)替换泛型类中的类型参数。
  • 如果使用了无界类型参数,则将泛型类的类型参数替换为Object。
  • 插入类型转换以保持类型安全。
  • 生成桥接方法以将多态保持在扩展的泛型类型中。

有界类型擦除(Bound Types Erasure)

如果使用有界的类型参数,Java编译器会用其界限类替换泛型类的类型参数。

例子

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> integerBox = new Box<Integer>();
      Box<Double> doubleBox = new Box<Double>();

      integerBox.add(new Integer(10));
      doubleBox.add(new Double(10.0));

      System.out.printf("Integer Value :%d\n", integerBox.get());
      System.out.printf("Double Value :%s\n", doubleBox.get());
   }
}

class Box<T extends Number> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }   
}

在这种情况下,java编译器会将T替换为Number类,并且在类型擦除之后,编译器将为以下代码生成字节码。

public class GenericsTester {
   public static void main(String[] args) {
      Box integerBox = new Box();
      Box doubleBox = new Box();

      integerBox.add(new Integer(10));
      doubleBox.add(new Double(10.0));

      System.out.printf("Integer Value :%d\n", integerBox.get());
      System.out.printf("Double Value :%s\n", doubleBox.get());
   }
}

class Box {
   private Number t;

   public void add(Number t) {
      this.t = t;
   }

   public Number get() {
      return t;
   }   
}

在两种情况下,结果都是相同的。

输出

Integer Value :10
Double Value :10.0

无界类型擦除(Unbounded Types Erasure)

如果使用无界的类型参数,则Java编译器会将泛型类的类型参数替换为Object。

例子

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> integerBox = new Box<Integer>();
      Box<String> stringBox = new Box<String>();

      integerBox.add(new Integer(10));
      stringBox.add(new String("Hello World"));

      System.out.printf("Integer Value :%d\n", integerBox.get());
      System.out.printf("String Value :%s\n", stringBox.get());
   }
}

class Box<T> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }   
}

在这种情况下,java编译器将用Object类替换T,并且在类型擦除之后,编译器将为以下代码生成字节码。

public class GenericsTester {
   public static void main(String[] args) {
      Box integerBox = new Box();
      Box stringBox = new Box();

      integerBox.add(new Integer(10));
      stringBox.add(new String("Hello World"));

      System.out.printf("Integer Value :%d\n", integerBox.get());
      System.out.printf("String Value :%s\n", stringBox.get());
   }
}

class Box {
   private Object t;

   public void add(Object t) {
      this.t = t;
   }

   public Object get() {
      return t;
   }   
}

在两种情况下,结果都是相同的。

输出

Integer Value :10
String Value :Hello World

泛型方法擦除(Generic Methods Erasure)

如果使用无界的类型参数,则Java编译器将泛型类的类型参数替换为对象,如果使用有界的类型参数用作方法参数,则Java编译器将使用其界限类替换类型参数。

例子

	public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> integerBox = new Box<Integer>();
      Box<String> stringBox = new Box<String>();

      integerBox.add(new Integer(10));
      stringBox.add(new String("Hello World"));
      
      printBox(integerBox);
      printBox1(stringBox);
   }
   
   private static <T extends Box> void printBox(T box) {
      System.out.println("Integer Value :" + box.get());
   }
   
   private static <T> void printBox1(T box) {
      System.out.println("String Value :" + ((Box)box).get());
   }
}

class Box<T> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }   
}

类型擦除之后,编译器将为以下代码生成字节码。

public class GenericsTester {
   public static void main(String[] args) {
      Box integerBox = new Box();
      Box stringBox = new Box();

      integerBox.add(new Integer(10));
      stringBox.add(new String("Hello World"));
      
      printBox(integerBox);
      printBox1(stringBox);
   }
	
   //Bounded Types Erasure
   private static void printBox(Box box) {
      System.out.println("Integer Value :" + box.get());
   }
	
   //Unbounded Types Erasure
   private static void printBox1(Object box) {
      System.out.println("String Value :" + ((Box)box).get());
   }
}

class Box {
   private Object t;

   public void add(Object t) {
      this.t = t;
   }

   public Object get() {
      return t;
   }   
}

在两种情况下,结果都是相同的。

输出量

Integer Value :10
String Value :Hello World

泛型限制(Restrictions on Generics)

不允许基本类型(No Primitive Types)

  • 原始类型(Raw Types):定义时没有声明类型参数的类型,如Box是原始类型,Box<T>则是泛型,而不是原始类型。
  • 基本类型(Primitive Types):像int,char,byte等的类型。

使用泛型,基本类型不能作为类型参数传递。在下面给出的示例中,如果将int基本类型传递给box类,则编译器将生成一个警告。为了避免这种情况,我们需要传递Integer对象而不是int基本类型。

例子

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> integerBox = new Box<Integer>();

      //编译错误
      //ReferenceType
      //- Syntax error, insert "Dimensions" to complete
      //ReferenceType
      //Box<int> stringBox = new Box<int>();

      integerBox.add(new Integer(10));
      printBox(integerBox);
   }

   private static void printBox(Box box) {
      System.out.println("Value: " + box.get());
   }  
}

class Box<T> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }   
}

输出

Value: 10

不允许实例化(No Instance)

类型参数不能用于实例化方法内部的对象。

public static <T> void add(Box<T> box) {
   //compiler error
   //Cannot instantiate the type T
   //T item = new T();  
   //box.add(item);
}

要实现此类功能,请使用反射。

public static <T> void add(Box<T> box, Class<T> clazz) throws InstantiationException, IllegalAccessException{
   T item = clazz.newInstance();   // OK
   box.add(item);
   System.out.println("Item added.");
}

例子

public class GenericsTester {
   public static void main(String[] args) throws InstantiationException, IllegalAccessException {
      Box<String> stringBox = new Box<String>();
      add(stringBox, String.class);
   }  

   public static <T> void add(Box<T> box) {
      //compiler error
      //Cannot instantiate the type T
      //T item = new T();  
      //box.add(item);
   }

   public static <T> void add(Box<T> box, Class<T> clazz) 
      throws InstantiationException, IllegalAccessException{
      T item = clazz.newInstance();   // OK
      box.add(item);
      System.out.println("Item added.");
   }   
}

class Box<T> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }   
}

输出

Item added.

不允许使用静态字段(No Static field)

使用泛型时,类型参数不允许为静态。 由于静态变量在对象之间共享,因此编译器无法确定要使用哪种类型。 如果允许使用静态类型参数,请考虑以下示例。

例子

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> integerBox = new Box<Integer>();
	  	Box<String> stringBox = new Box<String>();
	  
      integerBox.add(new Integer(10));
      printBox(integerBox);
   }

   private static void printBox(Box box) {
      System.out.println("Value: " + box.get());
   }  
}

class Box<T> {
   //compiler error
   private static T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }   
}

由于stringBox和integerBox都有一个带有标记的静态类型变量,因此无法确定其类型。 因此,不允许使用静态类型参数。

不能强制转换(No Cast)

除非强制使用无界通配符对其进行参数化,否则不允许强制转换为参数化类型。

例子

Box<Integer> integerBox = new Box<Integer>();
Box<Number> numberBox = new Box<Number>();
//Compiler Error: Cannot cast from Box<Number> to Box<Integer>
integerBox = (Box<Integer>)numberBox;

要实现相同的目的,可以使用无界通配符。

private static void add(Box<?> box) {
   Box<Integer> integerBox = (Box<Integer>)box;
}

不能使用instanceOf(No instanceOf)

由于编译器使用类型擦除,因此运行时不跟踪类型参数,因此在运行时,无法使用instanceOf运算符来验证Box <Integer>和Box <String>之间的差异。

例子

Box<Integer> integerBox = new Box<Integer>();

//Compiler Error:
//Cannot perform instanceof check against 
//parameterized type Box<Integer>. 
//Use the form Box<?> instead since further 
//generic type information will be erased at runtime
if(integerBox instanceof Box<Integer>) { }

不允许使用参数化类型的数组(No Array)

不允许使用参数化类型的数组。

//Cannot create a generic array of Box<Integer>
Box<Integer>[] arrayOfLists = new Box<Integer>[2]; 

由于编译器使用类型擦除,因此会将类型参数替换为Object,并且用户可以将任何类型的对象添加到数组中。并且在运行时,代码将无法引发ArrayStoreException。

例子

// compiler error, but if it is allowed
Object[] stringBoxes = new Box<String>[];
  
// OK
stringBoxes[0] = new Box<String>();  

// An ArrayStoreException should be thrown,
// but the runtime can't detect it.
stringBoxes[1] = new Box<Integer>();  

不允许扩展Throwable类(No Exception)

泛型类不允许直接或间接扩展Throwable类。

//The generic class Box<T> may not subclass java.lang.Throwable
class Box<T> extends Exception {}

//The generic class Box<T> may not subclass java.lang.Throwable
class Box1<T> extends Throwable {}

不允许方法捕获类型参数的实例。

public static <T extends Exception, J> void execute(List<J> jobs) {
      try {
         for (J job : jobs) {}
  
         // compile-time error
         // Cannot use the type parameter T in a catch block
      } catch (T e) { 
         // ...
   }
} 

在throws子句中允许使用类型参数。

class Box<T extends Exception>  {
   private int t;

   public void add(int t) throws T {
      this.t = t;
   }

   public int get() {
      return t;
   }   
}

不允许重载(No Overload)

一个类不允许具有两个在类型擦除后可以具有相同签名的重载方法。

例子

class Box  {
   //Compiler error
   //两个print方法在方法擦除后具有同一个方法签名
   public void print(List<String> stringList) { }
   public void print(List<Integer> integerList) { }
}

空文件

简介

Java泛型相关,Java泛型教程 展开 收起
取消

发行版

暂无发行版

贡献者

全部

语言

近期动态

不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Java
1
https://gitee.com/theten52/java-generics-tutorial.git
git@gitee.com:theten52/java-generics-tutorial.git
theten52
java-generics-tutorial
java-generics-tutorial
master

搜索帮助