【问题标题】:covariant return type for default method默认方法的协变返回类型
【发布时间】:2017-12-14 22:06:25
【问题描述】:

我可以覆盖具有协变返回类型的方法,但是是否可以覆盖具有协变返回类型的默认方法?在下面的示例中,我想覆盖 getFirstLeg 而不重写默认方法,但 Java 不允许这样做。我也不想让 Animal 成为一个通用接口,因为可能有很多参数,而且 Animal 用在很多地方。

interface Leg {
}

interface Animal {
    List<? extends Leg> getLegs();

    default Leg getFirstLeg() {
        return getLegs().get(0);
    }
}

abstract class AnimalImpl<T extends Leg> implements Animal {
    private List<T> legs;

    @Override
    public List<T> getLegs() {
        return legs;
    }
}

interface DuckLeg extends Leg {
}

interface Duck extends Animal {
    @Override
    List<? extends DuckLeg> getLegs(); //covariant return type

    @Override
    DuckLeg getFirstLeg(); //I do not want to rewrite this method
}

class DuckImpl extends AnimalImpl<DuckLeg> implements Duck {
    //Error: java: DuckImpl is not abstract and does not override abstract method getFirstLeg() in Duck
}

更新:以下代码可以编译,但新问题是 Duck 不再是 Animal。

interface Leg {
}

interface AnimalGeneric<T extends Leg> {
    List<? extends T> getLegs();

    default T getFirstLeg() {
        return getLegs().get(0);
    }
}

abstract class AnimalImpl<T extends Leg> implements AnimalGeneric<T> {
    private List<T> legs;

    @Override
    public List<T> getLegs() {
        return legs;
    }
}

interface Animal extends AnimalGeneric<Leg> {
    //empty
}

interface BirdLeg extends Leg {
}

interface BirdGeneric<T extends BirdLeg> extends AnimalGeneric<T> {
}

class BirdImpl<T extends BirdLeg> extends AnimalImpl<T> implements BirdGeneric<T> {
}

interface Bird extends BirdGeneric<BirdLeg> {
    //empty
}

interface DuckLeg extends BirdLeg {
}

class DuckImpl extends BirdImpl<DuckLeg> implements Duck {
}

interface Duck extends BirdGeneric<DuckLeg> {
}

【问题讨论】:

    标签: java default-method covariant-return-types


    【解决方案1】:

    是的,Java 确实具有用于继承方法的协变返回类型。但这里的问题不是getFirstLegdefault 方法,而是Java 的泛型不是协变的;它们是不变的。 IE。 List&lt;DuckLeg&gt; 不是 List&lt;? extends Leg&gt;,即使 DuckLegLeg

    但您可以通过将Animal 接口设为通用来解决此问题,以便类型参数可以根据子接口进行更改。

    interface Animal<T extends Leg> {
        List<T> getLegs();
    
        default T getFirstLeg() {
            return getLegs().get(0);
        }
    }
    

    那么Duck 也有一个类型参数,但不再需要覆盖任何东西。类型参数DuckLeg 分配给T

    interface Duck extends Animal<DuckLeg> {
        // able to remove the override of getLegs
        //@Override
        //List<DuckLeg> getLegs(); //covariant return type
    
        // able to remove the override of getFirstLeg
        //@Override
        //DuckLeg getFirstLeg(); //I do not want to rewrite this method
    }
    

    那么您的DuckImpl 类将正确继承所有内容,并且您不会收到“非抽象且不覆盖抽象方法”错误。

    当然,对于Duck 的空声明,您可能根本不需要它。只需让DuckImpl 实现Animal&lt;DuckLeg&gt;

    class DuckImpl extends AnimalImpl<DuckLeg> implements Animal<DuckLeg> {
    
    }
    

    【讨论】:

    • 请看我帖子的最后一句话。我担心使 Animal(或任何其他广泛使用的接口)通用,因为如果我们想更改参数,我们将不得不更改很多地方。
    • 虽然 List 不是 List extends Leg>,不应该是 List extends DuckLeg> 是一个 List?
    • 使Animal 泛型是我看到的唯一解决问题的方法,因为Java 泛型是不变的。此外,A List&lt;? extends DuckLeg&gt; 不能是 List&lt;? extends Leg&gt;,因为通配符代表特定但未知的类型。 List&lt;? extends DuckLeg&gt; 可以代表List&lt;DuckLeg&gt;,而List&lt;? extends Leg&gt; 可以代表List&lt;TurkeyLeg&gt;。由于不匹配的可能性,编译器必须禁止使用通配符的协变泛型。
    • 如果是这样,为什么我可以使用 List getLegs() 以覆盖 List getLegs()?
    【解决方案2】:

    如果您需要协方差,请输入Leg。然后,您可以删除大部分代码。

    这样编译:

    interface Leg {
    }
    
    interface Animal<T extends Leg> {
        List<T> getLegs();
    
        default T getFirstLeg() {
            return getLegs().get(0);
        }
    }
    
    abstract class AnimalImpl<T extends Leg> implements Animal<T> {
        private List<T> legs;
    
        @Override
        public List<T> getLegs() {
            return legs;
        }
    }
    
    interface DuckLeg extends Leg {
    }
    
    interface Duck extends Animal<DuckLeg> {
    }
    
    class DuckImpl extends AnimalImpl<DuckLeg> implements Duck {
        // no errors
    }
    

    【讨论】:

    • 请看我帖子的最后一句话。我担心使 Animal(或任何其他广泛使用的接口)通用,因为如果我们想更改参数,我们将不得不更改很多地方。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-02
    • 1970-01-01
    • 1970-01-01
    • 2011-09-28
    • 1970-01-01
    • 1970-01-01
    • 2022-08-05
    相关资源
    最近更新 更多