如何将泛型与接口一起使用
How to use generics with interfaces
我对编程还很陌生,我们有一个练习,我们必须使用消费者接口,我们有一个通用的 class (genClass),它有一个 Consumer<T>
属性 (conAtt
).在另一个 class 中我们必须使用 Consumer 的 accept 方法,但不知何故它不起作用。我已经通读了Consumer接口的Java API,但没有帮助。
错误消息说:
The method accept(capture#4-of ?) in the type Consumer<capture#4-of ?> is not applicable for the arguments (capture#5-of ?)
我知道它说不适用,但为什么不呢?
public abstract class GenClass<T> {
protected Consumer<T> conAtt;
public abstract T getData();
}
class Otherclass{
private List<GenClass<?>> helparray= new ArrayList<>();
private void testmethod() {
Iterator<GenClass<?>> hilfe = helparray.iterator();
while (hilfe.hasNext()) {
GenClass<?> help = hilfe.next();
help.conAtt.accept(help.getData());//here is the problem
}
}
}
public class thirdclass extends GenClass<Character> {
@Override
public Character getData() {
return 't';//t is just an example
}
}
这实际上不是关于 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
class 中将 通用类型 指定为 T
- 这与您指定的类似使用 ?
(可能您还想将其替换为 T
,因为稍后在使用 Consumer
时您将 运行 处理其他问题)。但是当我们在其他 classes 中使用该实现时,就不可能再将其指定为 CustomList<T>
或 CustomList<?>
。我们需要决定并指定列表应该包含哪些确切类型的元素。这是 String
class,所以我们将其指定为 CustomList<String>
。
注意: ?
是一个 通用通配符 ,意思是“我不知道class现在是,我以后也不知道”。这就是为什么您以后在 Consumer
中使用具体类型会很困难。您将无法在其中的对象上调用任何具体方法。因此,应避免将 ?
作为 通用类型参数 ,而应使用 T
之类的东西。 T
的意思是“我现在不知道 classes 的真实类型,但我会等你告诉我后做”。因此,稍后您将能够在 Consumer
中调用对象的具体方法,这将大大简化您的工作。
对于您的代码,这意味着无论您想在何处使用 GenClass<T>
的实现,您都需要指定 class 将使用哪种元素。在 String
的情况下是 GenClass<String>
在 Character
的情况下 GenClass<Character>
.
所以您需要替换出现的 GenClass<?>
的地方是您在 Otherclass
和 Otherclass.testmethod
.
中引用它的地方
您使用 Consumer
的方式很好
我对编程还很陌生,我们有一个练习,我们必须使用消费者接口,我们有一个通用的 class (genClass),它有一个 Consumer<T>
属性 (conAtt
).在另一个 class 中我们必须使用 Consumer 的 accept 方法,但不知何故它不起作用。我已经通读了Consumer接口的Java API,但没有帮助。
错误消息说:
The method accept(capture#4-of ?) in the type Consumer<capture#4-of ?> is not applicable for the arguments (capture#5-of ?)
我知道它说不适用,但为什么不呢?
public abstract class GenClass<T> {
protected Consumer<T> conAtt;
public abstract T getData();
}
class Otherclass{
private List<GenClass<?>> helparray= new ArrayList<>();
private void testmethod() {
Iterator<GenClass<?>> hilfe = helparray.iterator();
while (hilfe.hasNext()) {
GenClass<?> help = hilfe.next();
help.conAtt.accept(help.getData());//here is the problem
}
}
}
public class thirdclass extends GenClass<Character> {
@Override
public Character getData() {
return 't';//t is just an example
}
}
这实际上不是关于 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
class 中将 通用类型 指定为 T
- 这与您指定的类似使用 ?
(可能您还想将其替换为 T
,因为稍后在使用 Consumer
时您将 运行 处理其他问题)。但是当我们在其他 classes 中使用该实现时,就不可能再将其指定为 CustomList<T>
或 CustomList<?>
。我们需要决定并指定列表应该包含哪些确切类型的元素。这是 String
class,所以我们将其指定为 CustomList<String>
。
注意: ?
是一个 通用通配符 ,意思是“我不知道class现在是,我以后也不知道”。这就是为什么您以后在 Consumer
中使用具体类型会很困难。您将无法在其中的对象上调用任何具体方法。因此,应避免将 ?
作为 通用类型参数 ,而应使用 T
之类的东西。 T
的意思是“我现在不知道 classes 的真实类型,但我会等你告诉我后做”。因此,稍后您将能够在 Consumer
中调用对象的具体方法,这将大大简化您的工作。
对于您的代码,这意味着无论您想在何处使用 GenClass<T>
的实现,您都需要指定 class 将使用哪种元素。在 String
的情况下是 GenClass<String>
在 Character
的情况下 GenClass<Character>
.
所以您需要替换出现的 GenClass<?>
的地方是您在 Otherclass
和 Otherclass.testmethod
.
您使用 Consumer
的方式很好