【问题标题】:How do I make my code work with Generics?如何使我的代码与泛型一起使用?
【发布时间】:2021-09-26 02:03:04
【问题描述】:

我目前正在学习泛型,首先我创建了一个用整数创建 stackArrayList 的类,之后我希望它被每个数字使用,所以我确实将数字放在方法中的任何地方,它有点工作但我认为这并不完全正确,我最大的问题是我不能对班级编号使用任何加法或减法。

所以我尝试实现 N extends Number 但这也不起作用,因此我什至不能将从方法 pop() 返回的 Number 放入变量中,告诉我 pop() 必须返回一个然后 int。

有人可以向我解释一下,我在那里做了什么吗?我能理解它背后的逻辑,但不能完全理解它。

public class StackArrayList<N extends Number> {

    private ArrayList<N> stackList = new ArrayList<>();
    
    
    
    public void push (N value) {
        if(value == null) {
            System.out.println("value null not allowed");
            return;
        }
        stackList.add(0,value);
        
    }
    public N pop() {
        N popInt = stackList.get(0);
        stackList.remove(0);
        return popInt;
    }
    public N addFirstTwo() {
        if(stackList.size()==1) {
            return stackList.get(0);
        }
        if (stackList.size() == 0) {return null;}
        
        
        if(stackList.get(0) instanceof Integer) {
            N summe;
            summe = 
            push(summe);
            return summe;
        }else if(stackList.get(0) instanceof Double){
            summe = pop().doubleValue()+pop().doubleValue();
            push(summe);
            return summe;
        }else if(stackList.get(0) instanceof Float){
            summe = pop().floatValue()+pop().floatValue();
            push(summe);
            return summe;
        }else if(stackList.get(0) instanceof Long){
            summe = pop().longValue()+pop().longValue();
            push(summe);
            return summe;
        }
        
        push(summe);
        return summe;
    }
    
}

【问题讨论】:

    标签: java list class generics extends


    【解决方案1】:

    问题在于Number 不支持+ 运算符,尽管它的所有实现(DoubleFloat 等)都支持它。

    您可以做的是为您的类定义一个内部接口,该接口公开一个sum 方法:

    interface SumPerformer<N> {
        N sum(N n1, N n2);
    }
    

    然后,您将在 StackArrayList&lt;N extends Number&gt; 类中声明一个包含此接口实现的字段:

    private final SumPerformer<N> sumPerformer;
    

    因此,您将使用此实现返回 addFirstTwo() 方法中的前两个数字的总和:

    public N addFirstTwo() {
        if (stackList.isEmpty()) {
            return null;
        }
        if (stackList.size() == 1) {
            return stackList.get(0);
        }
        return sumPerformer.sum(stackList.get(0), stackList.get(1));
    }
    

    当需要使用Number 的具体实现来初始化您的StackArrayList 时,您只需通过方法引用将实现的sum 传递给它:

        StackArrayList<Double> stackArrayListDoubles = new StackArrayList<>(Double::sum);
        StackArrayList<Long> stackArrayListLongs = new StackArrayList<>(Long::sum);
        StackArrayList<Integer> stackArrayListInts = new StackArrayList<>(Integer::sum);
    

    完整代码:

    public static final class StackArrayList<N extends Number> {
    
        private final List<N> stackList = new ArrayList<>();
        private final SumPerformer<N> sumPerformer;
    
        public StackArrayList(SumPerformer<N> sumPerformer) {
            this.sumPerformer = sumPerformer;
        }
    
        public N pop() {
            N popInt = stackList.get(0);
            stackList.remove(0);
            return popInt;
        }
    
        public void push(N value) {
            if (value == null) {
                System.out.println("value null not allowed");
                return;
            }
            stackList.add(0, value);
    
        }
    
        public N addFirstTwo() {
            if (stackList.isEmpty()) {
                return null;
            }
            if (stackList.size() == 1) {
                return stackList.get(0);
            }
            N sum = sumPerformer.sum(stackList.get(0), stackList.get(1));
            push(sum);
            return sum;
        }
    
        interface SumPerformer<N> {
            N sum(N n1, N n2);
        }
    
    }
    

    对 cme​​ts 中提出的问题的回答

    变量不需要在里面吗?

    没有。 &lt;&gt;(称为菱形运算符)用于将特定类型推断为采用“通用”类型的类(您可以阅读more about generics)。

    在 Java 8 之前,您需要在变量的声明和实例化中指定类型。例如接口List&lt;T&gt;由类ArrayList&lt;T&gt;实现。

    在 Java 8 之前,如果您想要一个 Integer 列表,您需要这样做:

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

    Java 8 之后,可以简单说明:

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

    确实,Integer 类型将通过声明 List&lt;Integer&gt; 推断为 ArrayList

    这就是为什么当我初始化我的StackArrayList&lt;&gt; 时,我没有将Double 传递给它,因为我已经将它传递给了声明,所以变量已经知道它的类型。

    它不应该只传递我初始化对象的数字吗?

    小心,你把东西弄混了。 上面解释了你不应该通过Double 的原因。 不过,这里我正在做其他事情。

    如果您检查StackArrayList 的新构造函数,我添加了一个成员private final SumPerformer&lt;N&gt; sumPerformer;

    这个成员是final,这意味着它应该在你初始化类时被初始化,我通过将它传递给构造函数来做到这一点。

    但是我传递给构造函数的是什么? 我正在传递我定义的抽象接口的具体实现

    具体来说,我已经声明了接口SumPerformer&lt;N&gt; 只公开一个称为sum 的方法,返回一个N 并接受两个不同的N 输入。

    因此,此接口的具体实现应该是具有相同签名的方法。

    而这个方法,是所有Number孩子的sum()方法(DoubleInteger...)

    如果你进入Double类,你会看到有这个方法:

    public static double sum(double a, double b) {
        return a + b;
    }
    

    N extends Number 类型为Double 时,上述方法的签名与您的接口匹配。

    因此,在您的课程中,您只需调用接口.sum() 来总结您的两个数字,然后将实现细节(基本上是a + b)留给您没有完成的具体实现,但是由 Java 本身在扩展 Number 的 JDK 类中实现。

    【讨论】:

    • @LuckyBandit74 当我初始化 StackArrayList 时,我传递了方法引用“Double::sum”。这已经是同一个接口的实现了。
    • @LuckyBandit74 Double 类(以及 Long、Float、Integer..)有一个签名为 N sum(N n1, N n2) 的方法。它们没有显式实现您的接口,但签名完美匹配,因此您可以将其用作抽象接口的具体实现。
    • @LuckyBandit74 Override 注解是一个可选注解。拥有它是一个很好的做法(如果缺少它,许多代码异味工具会抛出错误),但这不是强制性的,编译器仅依赖于签名匹配。在你的情况下,它很方便。
    • @DavidRumpf 这不是天才,它只是 Java :) 我已经用你的 cmets 的答案更新了答案,如果还不清楚,请告诉我。
    • @DavidRumpf 是的,这种语法有一个名字,叫做method reference
    猜你喜欢
    • 2020-02-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-02
    • 2016-04-07
    • 2018-10-23
    • 2011-02-09
    相关资源
    最近更新 更多