Java多接口泛型对象转换

10
假设我有一个通用类,声明如下:
public class ConfigurableRuleKey<R extends Configurable & Rule> extends Key<R> {

    private final R rule

    public ConfigurableRuleKey(R rule) {
        this.rule = rule;
    }

    /* Additional methods are declared here */

}

我希望实现一个工厂方法,检查传递的规则是否实现了接口Configurable,在创建可配置规则或仅创建基本键时使用。

public static <R extends Rule> Key<R> create(R rule) {
    if (rule instanceof Configurable) {
        return new ConfigurableRuleKey<>(rule); //This will not compile
    } else {
        return new RuleKey<>(rule);
    }
}

问题在于我的工厂方法无法将规则传递给ConfigurableRuleKey的构造函数,因为它不符合声明的泛型约束(即使我已经明确检查它实现了Configurable)。问题是如何将我的规则实例强制转换,以便符合ConfigurableRuleKey的构造函数限制?

2个回答

3

我找到了一种不使用原始类型来完成您要求的方法,但仍需要进行一对未经检查的强制类型转换:

@SuppressWarnings("unchecked")
public static <R extends Rule, T extends Rule & Configurable> Key<R> create(R rule) {
    if (rule instanceof Configurable) {
        return (Key<R>)new ConfigurableRuleKey<>((T)rule);
    } else {
        return new RuleKey<>(rule);
    }
}

我不确定这是否比sp00m的答案更好,但至少有所不同。 :)

这个在Java的泛型中不能正确构建的内在原因似乎是交叉类型需要从具体接口中构建,而不能从其他类型变量中构建。因此,在这个例子中没有办法将T构建为由R限制的方式。没有了这个能力,甚至没有一个类似于Class.asSubclass的神奇方法来抽象这个行为。

编辑:相关地,Java 8引入了匿名交集类型,可以将其强制转换为多个接口--例如(Configurable & Rule)rule--但即使如此,由于上述相同的原因,也无法强制转换为(Configurable & R)。然而,它可以帮助消除类型变量T

@SuppressWarnings("unchecked")
public static <R extends Rule> Key<R> create(R rule) {
    if (rule instanceof Configurable) {
        return (Key<R>)new ConfigurableRuleKey<>((Configurable & Rule)rule);
    } else {
        return new RuleKey<>(rule);
    }
}

我的集成开发环境告诉我这段代码甚至无法编译,但实际上它似乎可以。 :) 谢谢。我想我会向我的IDE支持团队报告此问题。 - Alex
@SimY4:只是出于好奇,IDE 抱怨的是什么? - Dolda2000
我正在使用 IntelliJ Idea 14 的早期访问预览版。 - Alex

2
这应该符合你的需求:
@SuppressWarnings({ "rawtypes", "unchecked" })
public static <R extends Rule> Key<R> create(R rule) {
    if (rule instanceof Configurable) {
        return new ConfigurableRuleKey((Configurable) rule);
    } else {
        return new RuleKey<>(rule);
    }
}

您手动检查了ruleConfigurable属性,因此转换是安全的。


@WilQu:我怀疑在不破坏泛型的情况下,OP想要的是不可能实现的,因为为了直接实例化ConfigurableRuleKey,需要一个既是Configurable又是Rule的具体类型,而create函数没有访问任何这样的具体类型。 - Dolda2000
1
如果您不使用泛型,则转换甚至都不必要。 - WilQu
@WilQu 是的,你可以自己试一下。 - sp00m
@WilQu 强制类型转换是必要的,你可以自己试试看。@sp00m 我会改进你的答案,将 @SuppressWarnings 移动到 if 分支中,以明确我们只想在该分支中禁止警告,而不是整个方法,因为方法的其余部分是正常的。 - icza
1
你也可以在转换中尝试使用(可配置和规则)。至少在Java 8中,这是合法的结构。 - NoDataFound
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接