抽象类中的Java变量和泛型问题

抽象类中的Java变量和泛型问题,java,generics,variadic-functions,Java,Generics,Variadic Functions,我在玩一些函数式的编程。还有一些嵌套很深的泛型的问题。下面是我的SCCE失败的例子,涉及到一个抽象类: public abstract class FooGen<IN, OUT> { OUT fn2(IN in1, IN in2) { // clever? try at a lazy way, just call the varargs version return fnN(in1, in2); } abstract OUT fnN(IN...i

我在玩一些函数式的编程。还有一些嵌套很深的泛型的问题。下面是我的SCCE失败的例子,涉及到一个抽象类:

public abstract class FooGen<IN, OUT> {

    OUT fn2(IN in1, IN in2) {  // clever? try at a lazy way, just call the varargs version
      return fnN(in1, in2);
   }

   abstract OUT fnN(IN...ins); // subclasses implement this

   public static void main(String[] args) {

      FooGen<Number, Number> foogen = new FooGen<Number, Number>() {   
         @Override Number fnN(Number... numbers) { 
            return numbers[0];
         }
      };

      System.out.println(foogen.fn2(1.2, 3.4));           
   }
}
但是,对于非抽象FooGen,它可以很好地工作:

public class FooGen<IN, OUT> {

      OUT fn2(IN g1, IN g2) { 
         return fnN(g1, g2); 
      }

      OUT fnN(IN...gs) {
         return (OUT)gs[0];
      }

   public static void main(String[] args) {
      FooGen<Number,Number> foogen = new FooGen<Number,Number>();
      System.out.println(foogen.fn2(1.2, 3.4)); 
   }
}
公共类FooGen{
输出fn2(在g1中,在g2中){
返回fnN(g1,g2);
}
输出fnN(输入…gs){
返回(输出)gs[0];
}
公共静态void main(字符串[]args){
FooGen FooGen=新FooGen();
System.out.println(foogen.fn2(1.2,3.4));
}
}
这个打印1.2。思想?Java似乎在某个地方失去了泛型的踪迹。这是对我泛型知识极限的挑战。:-)

(在回答问题时添加)

首先,感谢大家的投票,感谢保罗和戴蒙的有益回答

我仍然在想为什么它在第二版中可以作为数字使用,我有了一个见解。作为一个思维实验,让我们在某处添加一个
.doubleValue()
。你不能。在代码本身中,变量是INs,而不是数字。在
main()
中,它只是声明类型,
FooGen
,但没有地方添加代码

在版本2中,它实际上并不像数字那样“有效”。在内部,通过擦除,一切都是对象,正如保罗和达蒙所解释的,并且,羞怯地回头看,我自己很清楚。基本上,在这个复杂的示例中,我被
声明过度兴奋和误导


别以为我会为解决问题而烦恼。整个想法是懒惰。:-)为了提高效率,我创建了采用原语双精度(和整数)的并行接口和代码,在那里这个技巧可以很好地工作

Varargs参数是最重要的数组。因此,如果没有语法糖,您的代码将如下所示:

OUT fn2(IN in1, IN in2) {
    return fnN(new IN[] {in1, in2});
}

abstract OUT fnN(IN[] ins);
除了[]中新增的
将不合法,因为,由于。数组需要知道其组件类型,但是中的
在运行时已被擦除到其上限
对象

不幸的是,varargs调用隐藏了这个问题,在运行时,您有了相当于
fnN(新对象[]{in1,in2})
,而
fnN
已被覆盖以获取一个
Number[]

然而,对于非抽象的FooGen,它工作得很好

这是因为通过直接实例化
FooGen
,您没有覆盖
fnN
。因此,它在运行时接受
对象[]
,并且不会发生
ClassCastException

例如,即使
FooGen
不是
abstract
,此操作也会失败:

FooGen<Number, Number> foogen = new FooGen<Number, Number>() {
    @Override
    Number fnN(Number... gs) {
        return super.fnN(gs);
    }
};
System.out.println(foogen.fn2(1.2, 3.4));
如果希望保留varargs支持,可以将此方法视为实现细节并委托给它:

abstract OUT fnNImpl(List<? extends IN> ins);

public final OUT fnN(IN... ins) {
    return fnNImpl(Arrays.asList(ins));
}

abstract OUT fnNImpl(List此
ClassCastException
是由于Java的一个称为“类型擦除”的功能而发生的。编译泛型时会发生类型擦除。由于Java编译器在运行时无法知道泛型类的类型,因此它会将泛型对象编译为
对象的实例

在您的代码中,当编译
FooGen
时,
fnN(In…ins)
接收类型为
Object[]
的参数。然后尝试将其中一个对象向下转换为泛型类型
OUT
时,会出现
ClassCastException

这甚至没有提到这样一个事实,即在Java中创建这样的“通用数组”是被禁止的

以下是一段引自:

下面是另一个例子,说明了 忽略在中发出的有关阵列构造的警告 与变量参数列表连用

示例(varargs方法及其调用):
公开期末考试{
静态T[]方法_1(t1,t2){
返回方法_2(t1,t2);//未检查的警告
} 
静态T[]方法_2(T…args){
返回args;
} 
公共静态void main(字符串…参数){
String[]strings=method_1(“坏”、“业力”);//ClassCastException
} 
} 
警告:[未选中]未选中的T[]类型的泛型数组创建
varargs参数
返回方法_2(t1,t2);
^
在本例中,第一个方法调用第二个方法,第二个方法调用第二个方法 方法接受变量参数列表。为了调用varargs 方法编译器创建一个数组并将其传递给方法 本例中,要创建的数组是T[]类型的数组,该数组 是一个数组,其组件类型是一个类型参数 这种数组在Java中是禁止的,您将收到一个错误 如果您试图自己创建这样的数组,则返回消息


不。基本上是的,但是你可以计算单词,比较日期等。我没有考虑过这种方法。但是为什么非抽象版本#2可以工作?我认为它会有同样的问题。接下来,一旦我遇到错误并怀疑它涉及Java的非泛型数组,(感谢您的详细解释)这让我想知道第二个版本是如何工作的。
OUT fn2(IN in1, IN in2) {
    //safe because the array won't be exposed outside the list
    @SuppressWarnings("unchecked")
    final List<IN> ins = Arrays.asList(in1, in2);
    return fnN(ins);
}

abstract OUT fnN(List<? extends IN> ins);
abstract OUT fnNImpl(List<? extends IN> ins);

public final OUT fnN(IN... ins) {
    return fnNImpl(Arrays.asList(ins));
}
Example (of a varargs method and its invocation):

public final class Test {  
        static <T> T[] method_1(T t1, T t2) { 
            return method_2(t1, t2);                      // unchecked warning 
        } 
        static <T> T[] method_2( T... args) { 
            return args; 
        } 
        public static void main(String... args) { 
            String[] strings = method_1("bad", "karma");  // ClassCastException 
        } 
} 

warning: [unchecked] unchecked generic array creation of type T[] for
varargs parameter 
            return method_2(t1, t2);
                           ^