如何让我的代码使用泛型?
How do I make my code work with Generics?
我目前正在学习泛型,首先我制作了 Class,它用整数制作了一个 stackArrayList,之后我希望它被每个数字使用,所以我确实把数字放在方法的任何地方,它有点管用,但我认为这不太正确,我最大的问题是我不能对 Class 数字使用任何加法或减法。
所以我尝试实现 N extends Number,但这也不起作用,我什至无法将方法 pop() 中的 returns 的数字放入变量内部,告诉我那么 pop() 必须 return 一个 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;
}
}
问题是 Number
不支持 +
运算符,即使它的所有实现(Double
、Float
等)都支持它。
你可以做的是为你的 class 定义一个内部接口,它公开了一个 sum
方法:
interface SumPerformer<N> {
N sum(N n1, N n2);
}
然后,您将在 StackArrayList<N extends Number>
class 中声明一个包含此接口实现的字段:
private final SumPerformer<N> sumPerformer;
因此,您将使用此实现来 return 您的 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);
}
}
评论中提出的问题的答案
Doesn't the Variable need to be inside the <> ?
没有。 <>
(称为菱形运算符)用于将特定类型推断为采用“通用”类型的 class(您可以阅读 more about generics)。
在Java8之前,您需要在变量的声明和实例化中指定类型。例如,接口 List<T>
由 class ArrayList<T>
.
实现
在 Java 8 之前,如果您想要 Integer
的列表,您需要执行以下操作:
List<Integer> list = new ArrayList<Integer>();
在Java8之后,你可以简单的写:
List<Integer> list = new ArrayList<>();
实际上,类型 Integer
将被声明 List<Integer>
推断为 ArrayList
。
这就是为什么当我初始化 StackArrayList<>
时,我没有将 Double
传递给它,因为我已经将它传递给声明,所以变量已经知道它的类型是什么。
shouldnt it just pass down the Number i initialize the Object with ?
小心,你搞混了。
你不应该传递 Double
的原因在上面已经解释过了。
不过,我在这里做点别的。
如果您检查 StackArrayList
的新构造函数,我添加了一个成员 private final SumPerformer<N> sumPerformer;
。
这个成员是final
,这意味着它应该在你初始化class时被初始化,我通过将它传递给构造函数来做到这一点。
但是我传递给构造函数的是什么?
我正在传递我定义的抽象接口的具体实现。
具体来说,我已声明接口 SumPerformer<N>
仅公开一种方法,该方法称为 sum
,returns 和 N
并接收两个不同的输入 N
.
因此,此接口的具体实现应该是具有相同签名的方法。
而这个方法,是所有Number
children的sum()
方法(Double
,Integer
...)
如果你进入classDouble
,你会看到有这个方法:
public static double sum(double a, double b) {
return a + b;
}
当类型 N extends Number
为 Double
时,上述方法的签名与您的接口匹配。
因此,在您的 class 中,您只需调用接口 .sum()
来求和您的两个数字,并将实现细节(a + b
,基本上)留给具体实现不是由您完成,而是由 Java 本身在扩展 Number
.
的 JDK 的 class 中完成
我目前正在学习泛型,首先我制作了 Class,它用整数制作了一个 stackArrayList,之后我希望它被每个数字使用,所以我确实把数字放在方法的任何地方,它有点管用,但我认为这不太正确,我最大的问题是我不能对 Class 数字使用任何加法或减法。
所以我尝试实现 N extends Number,但这也不起作用,我什至无法将方法 pop() 中的 returns 的数字放入变量内部,告诉我那么 pop() 必须 return 一个 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;
}
}
问题是 Number
不支持 +
运算符,即使它的所有实现(Double
、Float
等)都支持它。
你可以做的是为你的 class 定义一个内部接口,它公开了一个 sum
方法:
interface SumPerformer<N> {
N sum(N n1, N n2);
}
然后,您将在 StackArrayList<N extends Number>
class 中声明一个包含此接口实现的字段:
private final SumPerformer<N> sumPerformer;
因此,您将使用此实现来 return 您的 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);
}
}
评论中提出的问题的答案
Doesn't the Variable need to be inside the <> ?
没有。 <>
(称为菱形运算符)用于将特定类型推断为采用“通用”类型的 class(您可以阅读 more about generics)。
在Java8之前,您需要在变量的声明和实例化中指定类型。例如,接口 List<T>
由 class ArrayList<T>
.
在 Java 8 之前,如果您想要 Integer
的列表,您需要执行以下操作:
List<Integer> list = new ArrayList<Integer>();
在Java8之后,你可以简单的写:
List<Integer> list = new ArrayList<>();
实际上,类型 Integer
将被声明 List<Integer>
推断为 ArrayList
。
这就是为什么当我初始化 StackArrayList<>
时,我没有将 Double
传递给它,因为我已经将它传递给声明,所以变量已经知道它的类型是什么。
shouldnt it just pass down the Number i initialize the Object with ?
小心,你搞混了。
你不应该传递 Double
的原因在上面已经解释过了。
不过,我在这里做点别的。
如果您检查 StackArrayList
的新构造函数,我添加了一个成员 private final SumPerformer<N> sumPerformer;
。
这个成员是final
,这意味着它应该在你初始化class时被初始化,我通过将它传递给构造函数来做到这一点。
但是我传递给构造函数的是什么? 我正在传递我定义的抽象接口的具体实现。
具体来说,我已声明接口 SumPerformer<N>
仅公开一种方法,该方法称为 sum
,returns 和 N
并接收两个不同的输入 N
.
因此,此接口的具体实现应该是具有相同签名的方法。
而这个方法,是所有Number
children的sum()
方法(Double
,Integer
...)
如果你进入classDouble
,你会看到有这个方法:
public static double sum(double a, double b) {
return a + b;
}
当类型 N extends Number
为 Double
时,上述方法的签名与您的接口匹配。
因此,在您的 class 中,您只需调用接口 .sum()
来求和您的两个数字,并将实现细节(a + b
,基本上)留给具体实现不是由您完成,而是由 Java 本身在扩展 Number
.