【问题标题】:Return type casting with generics and dynamic class type使用泛型和动态类类型返回类型转换
【发布时间】:2019-01-03 21:34:27
【问题描述】:

我试图避免从方法中显式转换返回值。

如果我在编译时静态指定类,我的效果很好,但我更喜欢在运行时从枚举类型中获取类。

public enum Type {

    SOME_TYPE(Clazz.class),
    ;

    private Type(Class<?> clazz) {
        this.clazz = clazz;
    }

    private final Class<?> clazz;

    public Class<?> getClazz() {
        return this.clazz;
    }
}

public class Foo {

    private Foo() {}

    public static <T> T doSomething(Class<T> clazz, String input) {
        // ...
        final Object obj = someProcess(input);

        return clazz.cast(obj);
    }
}

public class Bar {

    public void stuff() {

        // does not work without explicit casting to SomeClass
        // compiler error
        Clazz sc = Foo.doSomething(Type.SOME_TYPE.getClazz(), someInputData);

        // this does work, without an explicit cast
        Clazz sc2 = Foo.doSomething(Clazz.class, someInputData);
    }
}

我觉得这是不可能动态实现的,因为编译器无法确定 getClazz() 方法会返回什么类。

我怎样才能做到这一点?

【问题讨论】:

  • 泛型仅在编译时存在以延长编译时类型安全。
  • 对于您想要实现的目标,您似乎需要明确的演员表
  • @ControlAltDel 如果必须,我将只明确指定类(如sc2 选项),这种方式更难使用。我害怕可能是它......
  • 我已更新示例以返回 Clazz 类型的 scsc2,它应该一直如此。输入我的例子太快了...

标签: java class generics casting


【解决方案1】:

泛型允许您省略强制转换如果编译器可以证明该值将是操作的兼容类型。

在您工作的sc2 示例中,编译器可以看到您正在为Class 类传递Class 对象。 doSomething 承诺返回一个类型与类型标记匹配的对象(编译器也会验证),因此可以证明doSomething 在这种情况下将返回一个Class 实例;不需要演员表。

但是由于动态选择Class 实例的性质,编译器无法证明该类型与该赋值兼容。据编译器所知,您可能会加载将更改作为类型令牌提供的 Class 的代码,这将更改返回的内容。由于可能存在不兼容的赋值,这将引发异常,Java 的规则需要显式转换。

从逻辑上讲,没有办法完全按照您的要求进行操作,本质上是设计使然。

如果不了解更多关于您要完成的工作,就很难提出替代方案,因为任何替代方法都会有局限性,我不知道哪些是可以接受的。当然,最简单的情况是,如果有(或可能有)一个有用的通用基类或接口来处理您可能返回的所有内容;但我想如果是这样,你就不会看这种设计了。

【讨论】:

  • Clazz 是一种复杂类型,它与其他可能的类类型输入共享一个公共基类,但是不幸的是,要访问更高级别的方法,用户仍然需要强制转换。 Foo 是一种转换器,它接受 XML 数据并输出 POJO。 POJO 类取决于要转换的 XML 文档的类型,因此需要将类指定为输入。
  • 嗯...我想我在您的代码中将Clazz 误读为Class,因此答案中可能有一两行混淆。我再看一下,看看我能不能把它清理一下,但我认为关键点是一样的
【解决方案2】:

我试图避免从方法中显式转换返回值。

目前,编译器无法从中推断出任何东西

Type.SOME_TYPE.getClazz()

因为这个表达式引用了一个用通配符键入的Class

private final Class<?> clazz;

public Class<?> getClazz() {
    return this.clazz;
}

编译器如何猜测它需要将其转换为 SomeClass

枚举中定义的Class 绝对是太泛型了。
我认为这种设计不适合您的需求。
如果你想避免任何演员,更换

Type.SOME_TYPE.getClazz()

通过明确你想要的类的方法返回:

SomeClass.class

SomeClass sc =  Foo.doSomething(SomeClass.class, someInputData); 

【讨论】:

  • 它并没有真正改变你的答案,但请参阅上面的更新。我错误地输入了SomeClass,而它应该是Clazz...无论如何,它将使用Foo.doSomething()的客户端代码,我试图让输入支持的类类型更容易。
  • 如您所料,getClazz() 返回Class&lt;?&gt; 并没有改变。为了使其工作getClazz() 应该返回Clazz,但在这种情况下,没有值可以在枚举值的字段中定义它,因为所有转换都将完成到Clazz。为了使客户的事情变得简单,我建议不要使用枚举来满足这个要求,而是倾向于将类作为参数传递。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-31
相关资源
最近更新 更多