简体   繁体   English

如何使用 generics 与接口

[英]How to use generics with interfaces

I'm fairly new to programming and we do have an exercise, we have to use the Consumer interface, we have a generic class (genClass) that has a Consumer<T> attribute ( conAtt ).我对编程相当陌生,我们确实有一个练习,我们必须使用 Consumer 接口,我们有一个具有Consumer<T>属性( conAtt )。 In another class we have to use the accept method of Consumer, but somehow it doesn't work.在另一个 class 中,我们必须使用 Consumer 的 accept 方法,但不知何故它不起作用。 I have already read through the Java API of the Consumer interface, but it didn't help.我已经通读了消费者界面的 Java API ,但没有帮助。

The error message says:错误消息说:

The method accept(capture#4-of ?) in the type Consumer<capture#4-of ?> is not applicable for the arguments (capture#5-of ?)

I know it says not applicable, but why not?我知道它说不适用,但为什么不呢?

    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

    }

}

This is not really a question about how the Consumer - or other interfaces - in Java work, but about Generics .这并不是关于 Java 中的Consumer或其他接口如何工作的问题,而是关于Generics的问题。

Generics aim to simplify the way of writing code and avoid code repetitions. Generics 旨在简化编写代码的方式并避免代码重复。 Eg you need to do a similar task, but for different types you can write it once by using Generics instead of writing it over and over again, just with concrete types being replaced.例如,您需要执行类似的任务,但对于不同的类型,您可以使用Generics编写一次,而不是一遍又一遍地编写,只需替换具体类型即可。

For example one day you have the need to keep track of a list of Strings .例如,有一天您需要跟踪一个Strings列表。 As easy as that, your going ahead and implementing a solution for that, whereby the first implementation can look like the following (note: a very simplified example, but it'll show the purpose):就这么简单,您继续并为此实施解决方案,第一个实施可能如下所示(注意:一个非常简化的示例,但它会显示目的):

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];
    }
}

So you can use the above implementation of the List in your code like the following:因此,您可以在代码中使用上述List实现,如下所示:

public static void main(String[] args) {
    CustomListString listOfStrings = new CustomListString();
    listOfStrings.add("A");
    listOfStrings.add("B");
}

Simple, specific and sufficient!简单、具体、足够!

But the other day, you also have the requirement to keep track of a list of Integers .但是前几天,您还需要跟踪Integers列表。 What to do now?现在要做什么?

A way to solve this is to just repeat your previous approach and to implement another CustomList only for the Integers now.解决此问题的一种方法是重复您以前的方法并现在仅为Integers实现另一个CustomList Where the corresponding implementation would look like this (the implementation of CustomListString has been copied and all occurrences of String have been replaced by Integer ):相应的实现如下所示( 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];
    }
}

As you can imagine now already, this is not flexible and can be very cumbersome in the future.正如您现在已经可以想象的那样,这并不灵活,将来可能会非常麻烦。 This approach will require a new implementation of each type you want to store in the future.这种方法将需要您将来要存储的每种类型的新实现。 So you might end up to also create implementations like CustomListDouble , CustomListCharacter , ... and so on, in which only the type of the elements within the array change - nothing else which would be of importance!因此,您最终可能还会创建CustomListDoubleCustomListCharacter等实现,其中只有数组中元素的类型会发生变化 - 没有其他重要的!

This will additionally lead to the situation, that you'll duplicate a lot of similar code (like findNextFreeIndex() method would have been) and in case of a bugfix need to adjust it in a lot of places instead of in only one.这还会导致这样的情况,即您将复制许多类似的代码(例如findNextFreeIndex()方法),并且如果出现错误修复,则需要在很多地方而不是只在一个地方进行调整。

To solve this issue and remain the type safety in the CustomList.get method Generics have been introduced to Java!为了解决这个问题并在CustomList.get方法中保持类型安全Generics已被引入 Java!

With the Generics approach you'll be able to create a single implementation of the CustomList to store all of your data types without unnecessarily duplicating any shared, basic code and remain the type safety !使用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];
    }
}

Using the new list following the Generics approach we can use it like this now:使用遵循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
}

The generic CustomList can now also be reused for any other type and still provide type safety .通用CustomList现在也可以用于任何其他类型,并且仍然提供类型安全性


What does it mean for your implementation这对您的实施意味着什么

You can see how we specified the generic type in the CustomList class as T - this is similar like you specified it with ?您可以看到我们如何在CustomList class 中将泛型类型指定为T - 这与您指定的类似? (probably you'll also want to replace it with T , since you'll run into other issues later when working with the Consumer ). (可能您还想用T替换它,因为稍后在使用Consumer时会遇到其他问题)。 But when we used the implementation in our other classes, it wouldn't have been possible to specify it as CustomList<T> or CustomList<?> anymore.但是当我们在其他类中使用该实现时,就不可能再将其指定为CustomList<T>CustomList<?> We needed to decide and specifiy which exact type of elements the list should contain.我们需要决定并指定列表应该包含哪些确切类型的元素。 This has been the String class, so we specified it as CustomList<String> .这是String class,因此我们将其指定为CustomList<String>

Note: ?注意: ? is a generic wildcard and means something like "I don't know the real type of the classes now and I'll also don't know it in the future".是一个通用通配符,意思是“我现在不知道类的真实类型,将来也不知道”。 That's why it'll be hard for you working with the concrete types later in the Consumer .这就是为什么稍后在Consumer中使用具体类型会很困难的原因。 You'll be not able to call any conrete methods on your objects therein.您将无法在其中的对象上调用任何具体方法。 Therefore ?因此? should be avoided as a generic type argument and something like T should be used instead.应该避免作为泛型类型参数,而应该使用类似T的东西。 T means something like "I don't know the real type of the classes now, but I'll do later, as soon as you tell me". T的意思是“我现在不知道课程的真正类型,但我会稍后再做,只要你告诉我”。 Therfore you'll be able to call concrete methods on the objects later in the Consumer , what will simplify your work there a lot.因此,稍后您将能够在Consumer中调用对象的具体方法,这将大大简化您的工作。

For your code this means, wherever you want to use your implementation of GenClass<T> you need to specify with which exact kind of elements the class is going to work with.对于您的代码,这意味着,无论您想使用GenClass<T>的实现,您都需要指定 class 将使用的确切类型的元素。 In case of String it is GenClass<String> in case of Character GenClass<Character> .String的情况下是GenClass<String>Character GenClass<Character>的情况下。

So the place you'll need to replace the occurrences of GenClass<?> is wherever you refer to it in Otherclass and Otherclass.testmethod .因此,您需要替换GenClass<?>出现的地方是您在OtherclassOtherclass.testmethod中引用它的任何地方。

The way you used the Consumer is fine您使用Consumer的方式很好

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM