Java:具有通用方法的通用 class

Java: Generic class with generic method

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

以下代码片段经过简化 classes 以提高可见性:

我得到了以下摘要class:

public abstract class AbstractTestModel<T extends AbstractTestModel> {

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

  public <U extends AbstractTestModel<T>> U as(Class<U> type){
    AbstractTestModel<T> 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());
    }
  }

}

可能还有像这样的具体 classes:

public class ConcreteTestModel1 extends AbstractTestModel<ConcreteTestModel1> {

  public void doSomethingElse(){

  }
}

我也写了一个工厂class。该工厂从服务器下载一个 JSON 对象并实例化一个具体的 classes - 取决于 JSON 响应。这个具体的 classes 有许多用于测试 JSON 响应的辅助方法。问题是,工厂方法总是 returns 一个 "AbstractTestModel".

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

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);
  }
}

as(Class type) 方法应该检查给定的 class 是否有效,将 "this" 转换为所需的 class 和 return ,但它总是 return 是 AbstractTestModel。

如果我将 "as" 方法设为静态,或者如果我像这样摆脱通用 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());
    }
  }

}

...然后它工作正常,但当然我不能再 return 所有其他方法中的具体 class。

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

感谢您的任何提示,祝您有愉快的一天!

曼纽尔

问题是编译器需要在编译时推断类型。这段代码大部分是提供的,但为了演示,我在 doSomethingElse 中添加了一些输出,并添加了 ConcreteTestModelX extending ConcreteTestModel1 (Note 我从 as 方法中删除了类型 T 以探索它在进一步的探索性测试中与通用类型交互的方式 ).

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();
    }
}

您在测试中遇到的问题似乎是您用于模型的变量没有泛型,编译器然后剥离泛型请参阅此答案

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

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);
    }
}

感谢大家的回答,我终于找到了问题所在。其实很简单:

工厂需要 return "AbstractTestModel<❓>" 而不仅仅是 "AbstractTestModel"。我已经将 <❓> 添加到 returning AbstractTestModel 的每个方法中。现在它工作得很好 :) 谢谢大家。没有你找不到答案。