2017-03-05 56 views
4

这里压倒一切的抽象枚举法是编译正确编译错误,而与一般的返回类型

public enum SupportedConversions { 
    INTEGER { 
     @Override 
     public Integer convert(String ion, Object[] aa) { 
      return null; 
     } 
    }, 
    LONG { 
     @Override 
     public Long convert(final String ion, Object[] aa) { 
      return null; 
     } 
    }; 

    public abstract <T> T convert(String val, Object[] aa); 
} 

但是,当我的抽象函数参数更改为对象而不是数组的名单,我得到的编译错误说一码“方法不会从超类中覆盖”。而这种情况只有在返回类型为通用

例如糟糕的代码

public enum SupportedConversions { 
    INTEGER { 
     @Override 
     public Integer convert(String ion, List<Object> aa) { 
      return null; 
     } 
    }, 
    LONG { 
     @Override 
     public Long convert(final String ion, List<Object> aa) { 
      return null; 
     } 
    }; 

    public abstract <T> T convert(String val, List<Object> aa); 
} 

是否有一个原因到不行。似乎更像是Java中的一个错误

+1

这很奇怪,虽然我确定有一个完全合乎逻辑的理由。如果你从'List'中移除类型参数,那么编译器错误就会消失。或者,您可以将类型参数设置为“T”。数组的版本会发出如下警告:“未检查重写:返回类型需要未经检查的转换。找到'java.lang.Long',必需'T'” – Jeremy

+0

是的编译错误消失,如果我删除显式类型的列表压倒一切的方法。 – Sparky

回答

1

问题应该是“为什么第一个可以编译”而不是“为什么第二个失败”。
两者都坏了。

<T> T convert(String val, Object[] aa) 

方法签名说:“不管呼叫者替代T,此方法将返回一个兼容的结果”。这不是很有用,因为唯一有效的返回值是null,但至少,编译器会告诉您,当您尝试在以这种方式声明的方法内返回不兼容的结果时。

但子类覆盖此方法类似

Long convert(final String ion, Object[] aa) 
换句话说

,覆盖,有望返回任何调用者与总是返回Long的方法希望的方法。这首先应该感觉不对......当您返回null时,结果仍然是兼容的,但当您返回非nullLong值时,编译器甚至不会提醒您,因为Long值与宣布退货类型为Long

但是,编译器应该已经发出了关于方法声明本身的警告。为了演示这个问题,你可以用这个声明来编写

String s = SupportedConversions.LONG.convert("bla", null); 

并且编译器不会对象。如上所述,基本类型声明<T> T convert(…)承诺返回任何呼叫者假设的T,这里,T已被推断为String。这显然会在运行时中断,当实现返回一个Long实例。


为什么这个可以编译的原因是与pre-Generics代码的兼容性。目的是让具有不同“生殖”状态的图书馆互动。例如。您可以使用最近的jdk编译Java 1.4应用程序代码,即使某些类现在覆盖 - 泛型方法。

因此,您的子类中不使用泛型的convert方法被允许覆盖基类的convert方法。与此相反,像

Long convert(final String ion, List<Object> aa) 

方法声明是使用泛型,因此,不允许绕过通用类型系统。如果您使用原始类型List,您又有一个非泛型声明,可绕过泛型而不受影响。


如果您现在想说,假设这里的泛型行为是不合逻辑的,那么您并不孤单。不仅因为覆盖方法与覆盖的通用声明位于相同的编译单元(enum声明)之内,两者都在enum声明内,这是一种在Java 5之前不存在的语法结构(引入泛型)。

此外,最重要的方法利用协变返回类型Long RESP。 Integer其中删除方法声明的返回类型为Object,这也不能出现在Java 5之前的代码中。

但是这些(仍然)是规则。您应该在这里关注编译器警告。如果您没有收到警告(我知道Netbeans IDE的默认设置不严格),您应该尝试启用它们。


这段代码没有修正。 enum s不可能做什么。您可以删除类型参数T并让基类型的方法声明返回Object,但enum常量中的协变返回类型是不相关的,因为它们不属于public API的一部分。最好的选择是:

public interface SupportedConversions<T> { 
    SupportedConversions<Integer> INTEGER = (String ion, Object[] aa) -> { 
     return null; 
    }; 
    SupportedConversions<Long> LONG = (String ion, Object[] aa) -> { 
     return null; 
    }; 
    public abstract T convert(String val, Object[] aa); 
} 

resp。

public interface SupportedConversions<T> { 
    SupportedConversions<Integer> INTEGER = (ion, aa) -> { 
     return null; 
    }; 
    SupportedConversions<Long> LONG = (ion, aa) -> { 
     return null; 
    }; 
    public abstract T convert(String val, List<Object> aa); 
    // we can support both variants 
    public default T convert(String val, Object[] aa) { 
     return convert(val, Arrays.asList(aa)); 
    } 
}