名称冲突,覆盖失败,在 class 上实现两个具有相同擦除的接口

Name Clash, override fail, on a class implementing two interfaces with same erasure

我正在创建一个 class 来覆盖方法签名,其擦除在 2 个已实现的接口之间是相同的,但在泛型类型方面有细微差别(一个是 method-inferred 类型,另一个 inferred-class 类型)。我正在寻找一个简洁的解决方案。我只能编辑继承的 class,不能编辑原始的遗留界面。

为了展示案例,我做了一个抽象示例,以理解问题:

我得到了开发者的遗产 parent class:

public class Developer<C>{
    Rate<C> getRate(Taxes<C> tax){ /*...*/ }     
}

我还有一个 Rentable 遗留界面,签名几乎相同

public interface Rentable {
    <C> Rate<C> getRate(Taxes<C> taxes);  
}

由于开发人员不可出租,在我的模型中,我创建了一个特殊的 developer 既是开发商,又是可出租的 material.

public class OutsourcableDeveloper<C> 
                 extends Developer<C> 
                 implements Rentable{
   @Override
   public Rate<C> getRate(Taxes<C> taxes){ /*...*/}
}

然后我得到了臭名昭著的

Name clash: The method getRate(Developer.Taxes) of type OutsourcableDeveloper has the same erasure as getRate(Developer.Taxes) of type Rentable but does not override it

怎么去掉,所以OutsourcableDeveloper.getRate()隐藏了 开发商和可出租的。 getRate()?

似乎有点不合逻辑,让一个共同的覆盖失败,然后又不允许扩展两个签名,因为擦除是相等的。

其中一个超类型从 de 方法推断类型而另一个从 class 推断类型这一事实真的很重要吗,特别是当我不打算在我的实现中调用任何超类时?考虑到这种简化,是否有解决此问题的技巧?

编辑:我对我的实际问题打开了一个更抽象、更少 solution-oriented 的问题来讨论继承设计问题,我认为这是我遇到的实际问题的相关本质:

EDIT2:上一个问题引导我找到此处发布的答案

其实他们并不相等。因为任何Rentable-Instance都允许给定任何类型参数T,而OutsourcableDeveloper限制了。

当然你可以假设在你的情况下很容易使用

<C> Rate<C> getRate(Taxes<C> taxes);  

接口版本。但是,如果开发人员想要继承 OutsourceableDeveloper,他会感到多么困惑。根据 Developer 的定义,他可以假设方法 getRate 固定为 C 但实际上它可以突然取任何值。 -> 允许这样做会导致混淆。

我可以为您提供以下代码示例,它可能适合您的情况。虽然用起来肯定不方便。但是当您将所有方法转发给 OursourcableDeveloperRentable 时,这是可能的。评论应该解释它是如何工作的。

//This class itself can be added to any Developer-lists
public class OutsourcableDeveloper<C> extends Developer<C> {

    public final OutSourcableDeveloperRentable RENTABLE_INSTANCE = new OutSourcableDeveloperRentable();

    @Override
    public Rate<C> getRate(final Taxes<C> taxes) {
        // Simply forward to the more general getRate instance.
        return this.RENTABLE_INSTANCE.getRate(taxes);
    }

    public void exampleBehaviourA() {
        //Example for how you can make both objects behave equally.
    }

    // This class can be added to the lists requiring a Rentable
    // And the original value can be retrieved by both classes.
    public class OutSourcableDeveloperRentable implements Rentable {

        public final OutsourcableDeveloper<C> PARENT_INSTANCE = OutsourcableDeveloper.this;

        //This method is the one to implement because it is more general than
        //the version of OutsourcableDeveloper.
        @Override
        public <T> Rate<T> getRate(final Taxes<T> taxes) {
            // Do your work.
            return null;
        }

        public void exampleBehaviourA() {
            //Just an example - Maybe for you it makes for sence to
            //forward the method of Oursoursable-Developer to here.
            //Then all Behaviour would be found in this class.
            OutsourcableDeveloper.this.exampleBehaviourA();
        }

    }
}

好的,我找到了解决方法。它很笨拙,但如果架构不是很复杂,它会更容易,灵感来自我 自己的答案:

public class OutsourcableDeveloper<C> 
                 extends Developer<C> 
                 implements Rentable{

    /* This might not be needed if we don't need to extract C from taxes parameter */
   final Class<C> currencyClass;
   public OutsourcableDeveloper(Class<C> currencyClass){ this.currencyClass = currencyClass;}

   @Override
   public Rate<C> getRate(@SuppressWarnings("rawtypes") Taxes taxes){
        try{
            C taxesCurrency = (C) currencyClass.cast(taxes.getCurrency()); //IF actually needed getting the typed instance
            return new Rate<C>(taxesCurrency); //Or whatever processing
        } catch (ClassCastException e){
            throw new UnsupportedOperationException("OutsourcableDeveloper does not accept taxes in a currency that its not hims");
        }
   }
}

也可以在没有通用类型的情况下使用 "extends Developer",所以它是隐式原始的。但我们也放宽了非冲突方法的类型