JavaRush /Java 博客 /Random-ZH /Java中的泛型是什么

Java中的泛型是什么

已在 Random-ZH 群组中发布
你好!今天我们来谈谈泛型。我必须说你会学到很多新东西!不仅如此,接下来的几堂课也将专门讨论泛型。 Java 中的泛型是什么 - 1 因此,如果您对这个主题感兴趣,那么您很幸运:今天您将了解很多有关泛型的特性。好吧,如果没有,冷静下来,放松一下!:) 这是一个非常重要的话题,你需要了解它。让我们从一个简单的开始:“什么”和“为什么”。什么是仿制药? 泛型是带有参数的类型。 创建泛型时,您不仅指定其类型,还指定它应使用的数据类型。我想最明显的例子已经浮现在你的脑海里了——这就是ArrayList!我们通常在程序中创建它的方式如下:
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<String> myList1 = new ArrayList<>();
       myList1.add("Test String 1");
       myList1.add("Test String 2");
   }
}
正如您可能猜到的,列表的特殊性在于不可能将所有内容“塞”进去:它只适用于对象String。现在让我们简短回顾一下 Java 的历史,并尝试回答这个问题:“为什么?” 为此,我们自己编写 ArrayList 类的简化版本。我们的列表只能将数据添加到内部数组并接收此数据:
public class MyListClass {

   private Object[] data;
   private int count;

   public MyListClass() {
       this.data = new Object[10];
       this.count = 0;
   }

   public void add(Object o) {
       this.data[count] = o;
       count++;
   }

   public Object[] getData() {
       return data;
   }
}
假设我们希望列表仅存储数字Integer。我们没有仿制药。Integer我们无法在 中显式指定 check 的 o 实例add()。那么我们的整个类将只适用于Integer,并且我们将不得不为世界上存在的所有数据类型编写相同的类!我们决定依靠我们的程序员,只需在代码中留下注释,以便他们不会在其中添加任何不必要的内容:
//use it ONLY with Integer data type
public void add(Object o) {
   this.data[count] = o;
   count++;
}
一位程序员错过了这个注释,并无意中尝试将数字与字符串混合在列表中,然后计算它们的总和:
public class Main {

   public static void main(String[] args) {

       MyListClass list = new MyListClass();
       list.add(100);
       list.add(200);
       list.add("Lolkek");
       list.add("Shalala");

       Integer sum1 = (Integer) list.getData()[0] + (Integer) list.getData()[1];
       System.out.println(sum1);

       Integer sum2 = (Integer) list.getData()[2] + (Integer) list.getData()[3];
       System.out.println(sum2);
   }
}
控制台输出: 300 线程“main”中的异常 java.lang.ClassCastException:java.lang.String 无法在 Main.main(Main.java:14) 处转换​​为 java.lang.Integer 在这种情况下最糟糕的是什么?远不是程序员的不专心。最糟糕的是,错误的代码最终出现在我们程序的重要位置并成功编译。现在我们不会在编码阶段看到错误,而只会在测试阶段看到错误(这是最好的情况!)。 在开发后期修复错误会花费更多的钱和时间。 这正是泛型的优点:泛型类可以让不幸的程序员立即检测到错误。该代码根本无法编译!
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Integer> myList1 = new ArrayList<>();

       myList1.add(100);
       myList1.add(100);
       myList1.add("Lolkek");//error!
       myList1.add("Shalala");//error!
   }
}
程序员会立即“醒悟”并立即纠正自己。顺便说一下,我们不必创建自己的类List来看到这种错误。<Integer>只需从常规 ArrayList 中 删除类型括号 ( ) 即可!
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

      List list = new ArrayList();

      list.add(100);
      list.add(200);
      list.add("Lolkek");
      list.add("Shalala");

       System.out.println((Integer) list.get(0) + (Integer) list.get(1));
       System.out.println((Integer) list.get(2) + (Integer) list.get(3));
   }
}
控制台输出: 300 Exception in thread "main" java.lang.ClassCastException: java.lang.String无法在Main.main(Main.java:16)处转换为java.lang.Integer 也就是说,即使使用“本机”工具Java,你可能会犯这个错误并创建一个不安全的集合。但是,如果我们将此代码粘贴到 IDEa 中,我们会看到一条警告:“ Unchecked call to add(E) as a member of raw type of java.util.List ”它告诉我们,向 a 中添加元素时可能会出现问题。没有泛型的集合不是这样的。但是“原始类型”这个词是什么意思呢?直译是相当准确的——“原始类型”或“脏类型”。 Raw type是一个泛型类,其类型已从中删除。 换句话说,List myList1这是Raw type。相反的raw type是使用类型规范正确创建的generic type泛型类(也称为类)。parameterized type例如,List<String> myList1。您可能有一个问题:为什么允许使用它raw types?原因很简单。Java 的创建者留下了对该语言的支持raw types,以免造成兼容性问题。当 Java 5.0 发布时(泛型在这个版本中首次出现),很多代码已经使用raw types. 因此,这种可能性在今天仍然存在。我们在讲座中已经不止一次提到过Joshua Bloch的经典著作《Effective Java》。raw types作为该语言的创造者之一,他并没有忽视书中使用and的话题generic typesJava 中的泛型是什么 - 2本书第 23 章有一个非常雄辩的标题:“不要在新代码中使用原始类型。” 这是你需要记住的事情。使用泛型类时,切勿将它们转换generic typeraw type.

类型化方法

Java 允许您键入单独的方法,创建所谓的泛型方法。为什么这样的方法方便呢?首先,因为它们允许您使用不同类型的参数。如果相同的逻辑可以安全地应用于不同的类型,那么泛型方法是一个很好的解决方案。让我们看一个例子。假设我们有某种列表myList1。我们想要从中删除所有值,并用新值填充所有可用空间。这就是我们的带有泛型方法的类的样子:
public class TestClass {

   public static <T> void fill(List<T> list, T val) {
       for (int i = 0; i < list.size(); i++)
           list.set(i, val);
   }

   public static void main(String[] args) {

       List<String> strings = new ArrayList<>();
       strings.add("Старая строка 1");
       strings.add("Старая строка 2");
       strings.add("Старая строка 3");

       fill(strings, "Новая строка");

       System.out.println(strings);

       List<Integer> numbers = new ArrayList<>();
       numbers.add(1);
       numbers.add(2);
       numbers.add(3);

       fill(numbers, 888);
       System.out.println(numbers);
   }
}
注意语法,它看起来有点不寻常:
public static <T> void fill(List<T> list, T val)
返回类型前面有<T>,表示泛型方法。在本例中,该方法采用 2 个参数作为输入:一个对象列表 T 和另一个单独的对象 T。通过使用 <T>,实现了该方法的类型化:我们无法在其中传递字符串列表和数字。字符串和字符串的列表,数字和数字的列表,我们的对象Cat和另一个对象的列表Cat- 这是唯一的方法。该方法main()清楚地表明该方法fill()可以轻松地处理不同类型的数据。首先,它接受一个字符串列表和一个字符串作为输入,然后是一个数字列表和一个数字。 控制台输出: [Newline, Newline, Newline] [888, 888, 888] 想象一下,如果fill()我们需要 30 个不同类的方法逻辑,并且我们没有通用方法。我们将被迫为不同的数据类型编写相同的方法 30 次!但由于通用方法,我们可以重用我们的代码!:)

类型化类

您不仅可以使用 Java 中提供的通用类,还可以创建自己的类!这是一个简单的例子:
public class Box<T> {

   private T t;

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

   public T get() {
       return t;
   }

   public static void main(String[] args) {

       Box<String> stringBox = new Box<>();

       stringBox.set("Старая строка");
       System.out.println(stringBox.get());
       stringBox.set("Новая строка");

       System.out.println(stringBox.get());

       stringBox.set(12345);//ошибка компиляции!
   }
}
我们的类Box<T>(“盒子”)已键入。在创建过程中为其指定了数据类型 () 后<T>,我们将无法再将其他类型的对象放入其中。这可以在示例中看到。创建时,我们指定我们的对象将使用字符串:
Box<String> stringBox = new Box<>();
当我们在最后一行代码中尝试将数字 12345 放入框中时,我们收到编译错误!就像这样,我们创建了自己的泛型类!:) 今天的讲座到此结束。但我们并没有告别仿制药!在接下来的讲座中,我们将讨论更高级的功能,所以不要说再见!) 祝你学习顺利!:)
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION