“嗨,阿米戈!”

“嗨,艾莉!”

“今天,Rishi 和我将向大家介绍仿制药。”

“等等,我想我几乎什么都知道了。”

“几乎所有,但不是所有。”

“真的吗?好吧,我准备好倾听了。”

“那我们开始吧。”

“在 Java 中,泛型是具有类型参数的类。”

“至于为什么要发明泛型,看代码中的注释:”

例子
ArrayList stringList = new ArrayList();
stringList.add("abc"); // Add a string to the list
stringList.add("abc"); // Add a string to the list
stringList.add( 1 ); // Add a number to the list

for(Object o: stringList)
{
 String s = (String) o; // There will be an exception here when we get to the integer
}

如何使用泛型解决问题:

例子
ArrayList<String> stringList = new ArrayList<String>();
stringList.add("abc"); // Add a string to the list
stringList.add("abc"); // Add a string to the list
stringList.add( 1 ); // There will be a compilation error here

for(Object o: stringList)
{
 String s = (String) o;
}

“这段代码根本无法编译,并且在编译期间会注意到由于添加错误的数据类型而导致的错误。”

“是的,我已经知道了。”

“好,好。重复是学习之母。”

“但是 Java 的创建者在创建泛型时有点懒惰。他们没有制作带有参数的完整类型,而是进行了巧妙的优化。实际上,他们没有向泛型添加任何有关类型参数的信息。相反,所有的编译过程中会发生魔法。”

泛型代码
List<String> strings = new ArrayList<String>();
strings.add("abc");
strings.add("abc");
strings.add( 1); // Compilation error

for(String s: strings)
{
 System.out.println(s);
}
真正发生了什么
List strings = new ArrayList();

strings.add((String)"abc");
strings.add((String)"abc");
strings.add((String) 1); // Compilation error

for(String s: strings)
{
 System.out.println(s);
}

“那很光滑。”

“是的,但是这种方法有一个副作用。 泛型类内部不存储类型参数的信息。 这种方法后来被称为类型擦除。”

“换句话说,如果你有自己的带有类型参数的类,你就不能在类中使用关于它们的信息。”

泛型代码
class Zoo<T>
{
 ArrayList<T> pets = new ArrayList<T>();

 public T createAnimal()
 {
  T animal = new T();
  pets.add(animal)
  return animal;
 }
}
真正发生了什么
class Zoo
{
 ArrayList pets = new ArrayList();

 public Object createAnimal()
 {
  Object animal = new ???();
  pets.add(animal)
  return animal;
 }
}

“在编译期间,所有参数类型都被替换为 Object。并且在类内部没有关于传递给它的类型的信息。”

“是的,我同意,那不是最好的。”

“没那么可怕,我稍后会告诉你如何解决这个问题。”

但还有更多。Java 允许您为类型参数指定父类型。extends 关键字用于此。例如:

泛型代码
class Zoo<T extends Cat>
{
 T cat;

 T getCat()
 {
  return cat;
 }

 void setCat (T cat)
 {
  this.cat = cat;
 }

 String getCatName()
 {
  return this.cat.getName();
 }
}
真正发生了什么
class Zoo
{
 Cat cat;

 Cat getCat()
 {
  return cat;
 }

 void setCat(Cat cat)
 {
  this.cat = cat;
 }

 String getCatName()
 {
  return this.cat.getName();
 }
}

“注意两个事实:”

“首先,你不能只传递任何类型作为参数——你只能传递 Cat 或继承 Cat 的类。”

“其次,在 Zoo 类内部,类型 T 的变量现在可以调用 Cat 类的方法。 右侧的列解释了原因(因为 Cat 将在任何有 T 的地方被替换)”

“是的。如果我们说 Cat 或 Cat 的子类作为类型参数传递,那么我们可以肯定类型 T 将始终具有 Cat 类的方法。”

“嗯,这很聪明。”