如何在界面中使用泛型干净地处理未经检查的转换?

时间:2017-01-07 19:51:22

标签: java generics interface

简化代码示例

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,其接口的返回类型为。{1}}。我正在使用<T extends SomeBaseClass>来强制返回类型符合预期。

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

  

类型安全:类型为someMethod()的返回类型SomeClass   BestClass需要未经检查的转换才能符合T的要求   输入SomeInterface

除了静音警告之外,我可以通过定义界面来避免这种情况:

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

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

现在看来一切正常,但我觉得我的实现有点不对,因为我必须为这个接口定义的每个方法明确地抑制这个警告。

是否有一种干净的方式来设计这样的界面而没有警告,但不需要冗长的implements声明?

1 个答案:

答案 0 :(得分:2)

显示警告的原因是因为您的解决方案确实不是类型安全的。签名<T extends BaseConfigClass> 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();