Java:具有泛型方法的泛型类

时间:2017-04-29 09:12:22

标签: java unit-testing generics casting integration-testing

我正在为我的java应用程序编写集成测试框架,我遇到了一个我无法解决的问题。请让我解释一下:

以下代码段是简化的类,以提高可见性:

我得到了以下抽象类:

public class SomeTest {

  TestModelFactory testModelFactory;
  @Before
  public void init(){
    testModelFactory = new TestModelFactory();
  }
  @Test
  public void someTest(){
    AbstractTestModel anyModel = testModelFactory.createModel("someIdOfTheServer");

    //ERROR: this does not work! Cannot resolve method doSomethingElse():
    anyModel.doSomething().as(ConcreteTestModel1.class).doSomethingElse();

    //as() method returns AbstractTestModel instead of ConcreteTestModel1
    //this works:
    AbstractTestModel as = anyModel.as(ConcreteTestModel1.class);
    //this does not work:
    ConcreteTestModel1 asConcreteTestModel1 = anyModel.as(ConcreteTestModel1.class);
  }
}

可能有这样的具体课程:

public abstract class AbstractTestModel {

  /*
  Not possible to return the superclass anymore
  public T doSomething(){
    return getThis();
  }
  public T getThis(){
    return (T) this;
  }
  */

  public <U extends AbstractTestModel> U as(Class<U> type){
    AbstractTestModel obj = this;
    if (obj.getClass().isAssignableFrom(type)){
      return type.cast(obj);
    }else{
      throw new AssertionError("This (" + obj.getClass().getName() +")could not be resolved to the expected class " + type.getName());
    }
  }

}

我还写了一个Factory类。此工厂从服务器下载JSON对象并实例化其中一个concret类 - 具体取决于JSON响应。这个具体的类有许多帮助方法来测试JSON响应。问题是,工厂方法总是返回“AbstractTestModel”。

集成测试看起来像这样(简化):

{{1}}

作为(类类型)的方法应该检查给定的类是否有效,将“this”强制转换为所需的类并返回它,但它总是返回AbstractTestModel。

如果我将“as”方法设为静态,或者如果我摆脱这样的泛型类......

{{1}}

...然后它工作正常,但当然我不能再用所有其他方法返回具体类了。

感谢您阅读这篇长篇文章。 你知道我在这里做错了什么吗?有解决方案吗?

感谢您的任何小费,祝您有愉快的一天!

曼努埃尔

2 个答案:

答案 0 :(得分:1)

问题是编译器需要在编译时推断出类型。此代码主要是提供的,但为了演示,我在doSomethingElse添加了一些输出,并添加ConcreteTestModelX扩展ConcreteTestModel1 注意 < / strong> 我从T方法中移除了as类型,以探索在进一步的探索性测试中与通用类型进行交互的方式

public abstract class AbstractTestModel<T extends AbstractTestModel> {

    public T doSomething() {
        return getThis();
    }

    public T getThis() {
        return (T) this;
    }

    public <U extends AbstractTestModel> U as(Class<U> type) {
        if (getClass().isAssignableFrom(type)) {
            return type.cast(this);
        } else {
            throw new AssertionError("This (" + getClass().getName()
                + ") could not be resolved to the expected class " + type.getName());
        }
    }

}

class ConcreteTestModel1 extends AbstractTestModel<ConcreteTestModel1> {

    public void doSomethingElse() {
        System.out.println("This is \"" + getClass().getSimpleName() + "\" doing something else");
    }
}

class ConcreteTestModelX extends ConcreteTestModel1 {

}

并通过此测试

import org.junit.Test;

public class SomeTest {

    @Test
    public void someTest(){
        AbstractTestModel<ConcreteTestModel1> anyModel = new ConcreteTestModel1();
        ConcreteTestModel1 asConcreteTestModel1 = anyModel.as(ConcreteTestModel1.class);
        asConcreteTestModel1.doSomethingElse();

        AbstractTestModel anyModelX = new ConcreteTestModelX();
        ConcreteTestModel1 asConcreteTestModelX = (ConcreteTestModel1)anyModelX;
        asConcreteTestModelX.doSomethingElse();
    }
}

似乎你在测试中遇到的问题是你用于模型的变量是没有泛型的,然后编译器剥离泛型看到这个答案https://stackoverflow.com/a/18277337/7421645

基于此,我创建了一些新的测试来探索:

import org.junit.Test;

public class SomeTest {

    @Test
    public void concreteTest(){
        ConcreteTestModel1 asConcreteTestModel1 = getConcreteModel(new ConcreteTestModel1(), ConcreteTestModel1.class);
        asConcreteTestModel1.doSomethingElse();
    }

    @Test
    public void concreteExtendsTest(){
        ConcreteTestModel1 asConcreteTestModelX = getConcreteModel(new ConcreteTestModelX(), ConcreteTestModelX.class);
        asConcreteTestModelX.doSomethingElse();
    }

    private <T extends ConcreteTestModel1> T getConcreteModel(T anyModel, Class<T> classType) {
        return anyModel.as(classType);
    }

    @Test
    public void vanillaCastingTest(){
        AbstractTestModel anyModelX = new ConcreteTestModelX();
        ConcreteTestModel1 asConcreteTestModelX = (ConcreteTestModel1)anyModelX;
        asConcreteTestModelX.doSomethingElse();
    }

    @Test
    public void abstractGenericTest(){
        AbstractTestModel<ConcreteTestModel1> anyModel = new ConcreteTestModel1();
        ConcreteTestModel1 asConcreteTestModel1 = anyModel.as(ConcreteTestModel1.class);
        asConcreteTestModel1.doSomethingElse();
    }

    @Test
    public void failedGenericTest(){
        AbstractTestModel anyModel = new ConcreteTestModel1();
        ConcreteTestModel1 asConcreteTestModel1 = getAs(anyModel);
        asConcreteTestModel1.doSomethingElse();
    }

    private ConcreteTestModel1 getAs(AbstractTestModel<ConcreteTestModel1> anyModel) {
        return anyModel.as(ConcreteTestModel1.class);
    }
}

答案 1 :(得分:0)

感谢所有答案,我终于找到了问题。实际上很容易:

工厂需要返回&#34; AbstractTestModel&lt;❓&gt;&#34;而不只是&#34; AbstractTestModel&#34;。我添加了&lt;❓&gt;返回AbstractTestModel的每个方法。现在它工作得很好:)谢谢大家。没有你就无法找到答案。