我今天聊聊Java中的泛型, 它是一个广泛使用但讨论较少的主题。我们经常会使用它,但大多数开发人员并没有真正了解它。
Java开发中你们肯定都用过List或者ArrayList。那你们应该记得如何定义他们吧?
List<Integer> list = new ArrayList<>(); // 这里的Integer 就是使用了泛型
这就是我们声明的方式。所以,我们使用了泛型。这里,<Integer>是我们传递的指定类型。那是一个类型。在我们创建这样的列表后,您只能将整数添加到列表中。
那如果我们不指定类型呢?
List numList = new ArrayList<>(); //不指定类型
如果我们像上面这样定义列表,我们就可以将从 Object 超类扩展的任何类型的数据添加到列表中。
所以添加泛型后,我们可以实现此列表的类型安全。
泛型意味着参数化类型。Java 让我们创建一个类、接口和方法,可以在泛型域中与不同类型的数据(对象)一起使用。
泛型的优点是:
让我们举个例子来解释为什么我们需要泛型。
想象一下,您必须使用打印机类打印数字和文本。打印机有一种在创建数据时接受数据的方法。
在传统方式中,我们必须创建 2 个类,因为我们有 2 种数据类型:数字(整数)和文本(字符串)
public class TextPrinter { private final String data; public TextPrinter(String data) { this.data = data; } public void print() { System.out.println("print::: " + data); }}
public class NumberPrinter { private final Integer data; public NumberPrinter(Integer data) { this.data = data; } public void print() { System.out.println("print::: " + data); }}
使用:
public class GenericsMain { public static void main(String[] args) { NumberPrinter numberPrinter = new NumberPrinter(5); numberPrinter.print(); // 输出 print::: 5 TextPrinter textPrinter = new TextPrinter("Hello"); textPrinter.print(); // 输出 print::: Hello }}
有没有觉得代码重复了?唯一的区别就是数据类型不同!
下面我们利用泛型来改造一下,使它成为一个通用的类型。
public class Printer<T> { private final T data; public Printer(T data) { this.data = data; } public void print() { System.out.println("print::: " + data); }}
使用:
Printer<Integer> integerPrinter = new Printer<>(5);integerPrinter.print(); // 输出 print::: 5Printer<String> stringPrinter = new Printer<>("Hello");stringPrinter.print(); // 输出 print::: HelloPrinter<Double> doublePrinter = new Printer<>(45.34);doublePrinter.print(); // 输出 print::: 45.34Printer<Long> longPrinter = new Printer<>(5L);longPrinter.print();z //输出 print::: 5
现在我们就只写了一个类,T用来表示作为通用标准的类型。我们甚至可以为其他数据类型(例如 Double/Long)创建打印对象。代码可重用性是通过风格实现的。
我们还可以创建多个类型的通用类。如下:
public class MultiPrinter<T, V> { private final T data1; private final V data2; public MultiPrinter(T data1, V data2) { this.data1 = data1; this.data2 = data2; } public void print() { System.out.println("print::: " + data1 + " : " + data2); }}
MultiPrinter<Integer, String> multiPrinter = new MultiPrinter<>(5, "Hello");multiPrinter.print(); // 输出 print::: 5 : Hello
Java 类型命名约定:
这是泛型的高级版本。我们可以通过有界泛型来限制更多并实现更多类型安全。
假设我们有一个AnimalPrinter类,它只能打印动物详细信息。不允许与其他物体一起使用。如何实现这一目标?
public class Animal { private final String name; private final String color; private final Integer age; public Animal(String name, String color, Integer age) { this.name = name; this.color = color; this.age = age; } public String getName() { return name; } public String getColor() { return color; } public Integer getAge() { return age; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Animal animal = (Animal) o; return Objects.equals(name, animal.name) && Objects.equals(color, animal.color) && Objects.equals(age, animal.age); } @Override public int hashCode() { return Objects.hash(name, color, age); }}public class Cat extends Animal { public Cat(String name, String color, Integer age) { super(name, color, age); }}public class Dog extends Animal { public Dog(String name, String color, Integer age) { super(name, color, age); }}
public class AnimalPrinter<T extends Animal> { private final T animalData; public AnimalPrinter(T animalData) { this.animalData = animalData; } public void print() { System.out.println("Name::: " + animalData.getName()); System.out.println("Color::: " + animalData.getColor()); System.out.println("Age::: " + animalData.getAge()); }}
在这个类中,T 扩展 Animal 部分完成了工作!我们限制了狗和猫的通用性!
AnimalPrinter<Cat> animalPrinter1 = new AnimalPrinter<>(new Cat("Jim", "brown", 2));animalPrinter1.print();AnimalPrinter<Dog> animalPrinter2 = new AnimalPrinter<>(new Dog("Rocky", "black", 5));animalPrinter2.print();
假设我们想向打印机通用功能添加更多功能。我们可以这样实现。
public class AnimalPrinter<T extends Animal & Serializable> { ..................}
我使用 Serialized 接口提供了 Serialized 功能。这里有一些重要的事情需要记住。
通配符由问号?表示 在 Java 中,我们用它们来指代未知类型。这可以用作泛型的参数类型。然后它将接受任何类型。在下面的代码中,我使用通配符将任何对象的列表用作方法参数。
public static void printList(List<?> list) { System.out.println(list);}printList( Arrays.asList( new Cat("Jim", "brown", 2), new Dog("Rocky", "black", 5) ));printList(Arrays.asList(50, 60));printList(Arrays.asList(50.45, 60.78));// output:// [generics.Cat@b1fa3959, generics.Dog@62294cd9]// [50, 60]// [50.45, 60.78]
列表现在可以是任何类型!
考虑这个例子:
public static void printAnimals(List<Animal> animals) { animals.forEach(Animal::eat);}
如果我们想象Animal的子类型,例如Dog ,我们就不能将此方法与Dog列表一起使用,即使Dog是Animal的子类型。我们可以使用通配符来做到这一点。
public static void printAnimals(List<? extends Animal> animals) { ...}
现在,此方法适用于Animal类型及其所有子类型。
printAnimals( Arrays.asList( new Cat("Jim", "brown", 2), new Dog("Rocky", "black", 5) ));
这称为上限通配符,其中Animal类型是上限。
我们还可以指定具有下限的通配符,其中未知类型必须是指定类型的超类型。可以使用super 关键字后跟特定类型来指定下限。
例子:
public static void addIntegers(List<? super Integer> list){ list.add(new Integer(70));}
想象一下,我们需要一种采用不同数据类型并执行某些操作的方法。我们可以为此创建一个通用方法并重用它。
public static <T> void call(T data) { System.out.println(data);}call("hello");call(45);call(15.67);call(5L);call(new Dog("Rocky", "black", 5));/* output: hello 45 15.67 5 generics.Dog@62294cd9*/
如果我们想返回数据而不是 VOID,我们也可以这样做。
public static <T> T getData(T data) { return data;}System.out.println(getData("Test")); // 输出 Test
我们也可以在通用方法中接受多种数据类型。
public static <T, V> void getMultiData(T data1, V data2) { System.out.println("data 1: " + data1); System.out.println("data 2: " + data2);}getMultiData(50, "Shades of Grey");
本文链接:http://www.28at.com/showinfo-26-17651-0.htmlJava中的泛型,看完这个还不会,我倒立洗头!
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com