简体   繁体   中英

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

I am creating a class that overrides a method signature whose erasure is identical between 2 implemented interfaces, but with a minor difference in regards of the generic type (one is a method-inferred type, the other an inferred-class type). I am looking for a neat solution. I CAN ONLY edit the inherited class, not the original legacy interfaces.

To show the case, I made up an abstract sample, to understand the problem:

I got a Developer legacy parent class:

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

I also got a Rentable legacy interface, with an almost identical signature

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

As a developer is not rentable, in my model, I create an special developer which is both a Developer, and Rentable material.

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

and then I got the infamous

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

How can I get rid of it, so OutsourcableDeveloper.getRate() hides both Developer and Rentable. getRate()?

It seems a bit illogical to fail a common override but then disallowing extending both signatures as the erasures are equal.

Does it really matters so much the fact that one of the supertypes infers type from de method and the other from the class specially when I'm not going to call any super in my implementation? Is there perhaps a trick to overcome the issue given this simplification?

EDIT: I opened a more abstract, less solution-oriented to my actual problem, question to discuss the inheritance design problem which I believe is the correlated essence of the actual issue I am having: Why can't I extend an interface "generic method" and narrow its type to my inherited interface "class generic"?

EDIT2: Previous question lead me to the answer posted here

Well they are actually not equal. Because any Rentable-Instance allows any typeparameter T to be given, while the OutsourcableDeveloper restricts it.

Of course you can assume that in your case it is easy to use the

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

Version of the interface. But expect how confused a developer could be, if he wants to subclass OutsourceableDeveloper . From the definition of Developer he can assume that the Method getRate is fixed to C but actually it can suddenly take any value. -> allowing this would lead to confusion.

What i can offer you is the following code-example, which may be suitable for your case. Although it definitely will be inconvenient to use it. But as you forward all methods to the OursourcableDeveloperRentable it is possible. The comments should explain how it works.

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

    }
}

Ok, I found a way to solve it. It's clumpsy, but it's the easier one if the architecture is not very complex, inspired by my Why can't I extend an interface "generic method" and narrow its type to my inherited interface "class generic"? own answer:

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

It is also possible to play with "extends Developer" without the generic type, so it is implictly raw. but we loose typing for the non-conflicting methods as well

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM