【问题标题】:How to cleanly handle unchecked conversion with generics in an interface?如何在接口中使用泛型干净地处理未经检查的转换?
【发布时间】:2017-01-07 19:51:22
【问题描述】:

简化代码示例

public class SomeBaseClass {
}

public class SomeClass extends SomeBaseClass {
}

public interface SomeInterface {
public <T extends SomeBaseClass> T someMethod();
}

public class BestClass implements SomeInterface {
   // @SuppressWarnings("unchecked")
   @Override
   public SomeClass someMethod() {
      return null;
   }
}

更详细的代码示例

// Base config class
public class BaseConfigClass {
}

// Some interface for all config classes to implement
public interface SomeInterface {
public <T extends BaseConfigClass> T getConfig();
}

// Class 'A' Config
public class ClassAConfig extends BaseConfigClass {
public doThingOnlyClassAKnows() {
}
}

//Class 'A'
public class ClassA implements SomeInterface {
   // @SuppressWarnings("unchecked")
   @Override
   public ClassAConfig getConfig() {
      return null;
   }
}

// Class 'B' Config
public class ClassBConfig extends BaseConfigClass {
public doThingOnlyClassBKnows() {
}
}

// Class 'B'
public class ClassB implements SomeInterface {
   // @SuppressWarnings("unchecked")
   @Override
   public ClassBConfig getConfig() {
      return null;
   }
}

...

new ClassA().getConfig().doThingOnlyClassAKnows()
new ClassB().getConfig().doThingOnlyClassBKnows()

我有几个不同的SomeClass,它们扩展了SomeBaseClass,其中一个接口的返回类型为。我使用&lt;T extends SomeBaseClass&gt; 来强制返回类型符合预期。

问题是 Java 对此不满意并提供以下警告:

类型安全:someMethod() 的返回类型 SomeClass 从类型 BestClass 需要未经检查的转换以符合 T 从 输入一些接口

除了消除警告之外,我还可以通过这样定义接口来避免这种情况:

public interface SomeInterface<T extends SomeBaseClass> {
public T someMethod();
}

但这最终会变得非常混乱,因为这个接口有几个方法,每个方法都实现了不同的基本类型;最后我得到了一个冗长的implements 声明。

现在一切似乎都按我的预期工作,但我觉得我的实现有点错误,因为我必须明确禁止此接口定义的每个方法的此警告。

有没有一种简洁的方法来设计这样的界面而不会发出警告但不需要冗长的implements 声明?

【问题讨论】:

  • 为什么someMethod 是通用的?
  • 我很惊讶编译:public SomeClass someMethod() { 没有实现public &lt;T extends SomeBaseClass&gt; T someMethod(),是吗?
  • 是的,这有点可疑。您实际上只在一个地方使用T(在第一个代码示例中)。它可能应该替换为具体类型。
  • @Sotirios 因为我想返回一个扩展 SomeBaseClass 的类,以确保存在某些基本方法。
  • 子类型可以使其实现返回类型更加具体。

标签: java generics interface


【解决方案1】:

显示警告的原因是您的解决方案确实不是类型安全的。带有签名&lt;T extends BaseConfigClass&gt; T getConfig() 的方法可以返回 BaseConfigClass 的任何子类。特别是,以下代码将在没有警告的情况下编译:

SomeInterface a = new ClassA();
ClassBConfig bConfig = a.getConfig();
bConfig.doThingOnlyClassBKnows();

但会在运行时抛出ClassCastException。 (好吧,实际上,它会抛出一个NullPointerException,但我假设您在真实代码中替换了return null :-)。

有几个解决方案。首先,在您的示例中,您没有使用该接口。如果您删除SomeInterface,您仍然可以执行new ClassA().getConfig().doThingOnlyClassAKnows()。如果该接口确实从未使用过,请将其删除!

最干净的解决方案是您已经提到的解决方案:给接口一个类型参数,告诉它它将返回什么类型的对象。我知道你不喜欢这样,因为你需要很多类型参数,但这是最简洁的解决方案。

如果您真的不想添加类型参数,我会完全删除泛型:

public interface SomeInterface {
    public BaseConfigClass getConfig();
}

在子类中,你仍然可以返回更具体的类型:

public class ClassA implements SomeInterface {
    public ClassAConfig getConfig();
}

但如果你只有对接口的引用,你将需要添加一个类型转换:

SomeInterface a = new ClassA();
ClassAConfig aConfig = (ClassAConfig) a.getConfig();
aConfig.doThingOnlyClassAKnows();

【讨论】:

  • 感谢您引用一个遇到ClassCastException 的示例。我的失误是liskov substitution principle 的完美申请。您会添加对此的引用,并可能对其他人进行简短的解释吗?
  • @Zhro。我不确定这个原则是否真的适用。问题实际上不是子类,而是接口方法的签名。签名允许返回BaseConfigClass 的任何类型的子类,类型检查器期望BaseConfigClass 的任何子类,但无法确保它期望返回的子类与返回的子类相同。
  • 我接受这是最佳答案。我想我应该提出一个新问题来澄清这一原则。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-22
  • 1970-01-01
  • 2010-12-03
  • 1970-01-01
  • 2012-03-18
  • 2019-09-13
相关资源
最近更新 更多