这并不是关于Consumer(或其他接口)在Java 中如何工作的问题,而是关于Generics 的问题。
泛型旨在简化编写代码的方式并避免代码重复。例如。您需要执行类似的任务,但对于不同的类型,您可以使用 Generics 编写一次,而不是一遍又一遍地编写,只需替换具体类型即可。
例如,有一天您需要跟踪Strings 的列表。就这么简单,您继续并为此实施一个解决方案,第一个实施可能如下所示(注意:一个非常简化的示例,但它会显示目的):
public class CustomListString {
private String[] elements = new String[10];
public void add(String newElement) {
int nextFreeIndex = findNextFreeIndex();
elements[nextFreeIndex] = newElement;
}
public String get(int index) {
return elements[index];
}
}
因此,您可以在代码中使用上述List 的实现,如下所示:
public static void main(String[] args) {
CustomListString listOfStrings = new CustomListString();
listOfStrings.add("A");
listOfStrings.add("B");
}
简单、具体、充分!
但是前几天,您还需要跟踪Integers 的列表。现在该怎么办?
解决此问题的一种方法是重复您以前的方法,并现在仅为Integers 实施另一个CustomList。对应的实现如下所示(CustomListString 的实现已被复制,所有出现的String 都已替换为Integer):
public class CustomListInteger {
private Integer[] elements = new Integer[10];
public void add(Integer newElement) {
int nextFreeIndex = findNextFreeIndex();
elements[nextFreeIndex] = newElement;
}
public Integer get(int index) {
return elements[index];
}
}
正如您现在已经可以想象的那样,这并不灵活,将来可能会非常麻烦。这种方法将需要您将来要存储的每种类型的新实现。因此,您最终可能还会创建像 CustomListDouble、CustomListCharacter 等这样的实现,其中只有数组中元素的类型会发生变化 - 没有其他重要的!
这还会导致这样的情况,你会复制很多类似的代码(就像 findNextFreeIndex() 方法一样),如果出现错误修复,需要在很多地方而不是只在很多地方进行调整一个。
为了解决这个问题并保持CustomList.get 方法中的类型安全 Generics 已被引入Java!
使用Generics 方法,您将能够创建CustomList 的单个实现来存储您的所有数据类型,而无需复制任何共享的基本代码并保持类型安全!
public class CustomList<T> {
private Object[] elements = new Object[10]; // Java doesn't supprort easily support generic arrays, so using Object
// here. But the compiler ensures only elements of the generic type T
// will end up here
public void add(T newElement) {
int nextFreeIndex = findNextFreeIndex();
elements[nextFreeIndex] = newElement;
}
@SuppressWarnings("unchecked")
public T get(int index) {
return (T) elements[index];
}
}
使用遵循Generics 方法的新列表,我们现在可以像这样使用它:
public static void main(String[] args) {
CustomList<String> genericList = new CustomList<>();
genericList.add("Hello World");
genericList.add(5); // Compile error! Integer and String types cannot be mixed in
// a single instance of the list anymore => Nice, prevents errors!
genericList.get(0).substring(6); // No compile error, also the compiler knows Strings
// are contained in the list
}
通用 CustomList 现在也可以用于任何其他类型,并且仍然提供类型安全。
这对您的实施意味着什么
您可以看到我们如何在 CustomList 类中将 通用类型 指定为 T - 这与您在 ? 中指定的类似(可能您还想替换使用T,因为稍后在使用Consumer 时会遇到其他问题)。但是当我们在其他类中使用该实现时,不可能再将其指定为CustomList<T> 或CustomList<?>。我们需要决定并指定列表应该包含哪些确切类型的元素。这是String 类,因此我们将其指定为CustomList<String>。
注意:? 是一个通用通配符,意思是“我现在不知道类的真实类型,我也不知道”以后不知道了”。这就是为什么稍后在Consumer 中使用具体类型会很困难。您将无法在其中的对象上调用任何具体方法。因此应避免将? 作为通用类型参数,而应使用T 之类的东西。 T 的意思是“我现在不知道课程的真正类型,但我会在你告诉我的时候尽快做”。因此,您稍后将能够在 Consumer 中调用对象的具体方法,这将大大简化您的工作。
对于您的代码,这意味着,无论您想在哪里使用 GenClass<T> 的实现,您都需要指定该类将使用的确切类型的元素。在String 的情况下是GenClass<String> 在Character 的情况下是GenClass<Character>。
因此,您需要替换 GenClass<?> 的位置是您在 Otherclass 和 Otherclass.testmethod 中引用它的任何位置。
你使用Consumer的方式很好