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 的每个方法中。现在它工作得很好 :) 谢谢大家。没有你找不到答案。
我正在为我的 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 的每个方法中。现在它工作得很好 :) 谢谢大家。没有你找不到答案。