【问题标题】:Java Generics name clash, method not correctly overriddenJava 泛型名称冲突,方法未正确覆盖
【发布时间】:2017-01-17 00:36:30
【问题描述】:

我已经看到了不同的问题,但我仍然觉得这个话题很混乱。

我想做的就是有一个实现接口的抽象类,并有一个扩展这个抽象类的类,这样hard类需要实现getKommune()setKommune(Kommune kommune),而不是其他方法,因为那是在抽象类中。

我有如下界面。

public interface KommuneFilter {

    <E extends AbstractKommune<?>> void addKommuneFromCurrentUser(E e);

    Kommune getKommune();

    void setKommune(Kommune kommune);
}

还有这个抽象类

public abstract class AbstractKommune<E extends AbstractKommune<?>> implements KommuneFilter {
    @PrePersist
    void addKommuneFromCurrentUser(E e) {
            Kommune k = e.getKommune();
    }
}

我想这样使用它

    public class Person extends AbstractKommune<Person> {
        private Kommune kommune;             
        public void setKommune(Kommune kommune) {this.kommune=kommune;}
        public Kommune getKommune() {return kommune;}
    }

但是,我明白了

名称冲突: 的方法具有相同的擦除类型但不覆盖它

为什么它没有被正确覆盖?

更新

感谢@Bozho,解决方案是这样的:

public interface KommuneFilter<E extends AbstractKommune<?>> {
    public void addKommuneFromCurrentUser(E e);
}

public abstract class AbstractKommune<E extends AbstractKommune<?>> implements KommuneFilter<E> 

public class Person extends AbstractKommune<Person>

【问题讨论】:

    标签: java generics


    【解决方案1】:

    我建议使接口通用,而不仅仅是它的方法:

    interface KommuneFilter<E extends AbstractKommune<?>> { .. }
    

    然后

    public abstract class AbstractKommune<E extends AbstractKommune<?>> 
         implements KommuneFilter<E> 
    

    【讨论】:

    • 谢谢,这似乎有效。但是,也许您可​​以让我理解为什么这个解决方案有效,而不是我的。
    • @Shervin:因为在你原来的设计中,接口指定的方法是泛型方法,而你的抽象类对@Override的尝试不是一个。
    【解决方案2】:

    它是名称冲突而不是覆盖的原因是因为它不是。接口指定的方法是泛型方法;您的抽象类尝试覆盖它不是。

    重现您的问题的更简洁的代码如下:

    interface I {
        <E> void foo(E e);
    }
    
    class C<E> implements I {
        public void foo(E e) { // name clash at compile time!
        }
    }
    

    这里的问题是interface I 指定实现者必须提供一个泛型方法&lt;E&gt;foo(它可以是&lt;Integer&gt;foo&lt;Boolean&gt;foo 等),但是说C&lt;String&gt; 真的只有@987654327 @。

    解决此问题的一种方法是使C.foo 成为泛型方法,正确地将@Override 设为interface I 的泛型方法:

    interface I {
        <E> void foo(E e);
    }
    
    class C<E> implements I {
        @Override public <T> void foo(T t) {
        }
    
        public static void main(String args[]) {
            C<String> o = new C<String>();
            o.<Integer>foo(0);
            o.<Boolean>foo(false);
        }
    }
    

    你可以在上面的代码中看到它做了什么:你有一个泛型类型C&lt;E&gt; 和一个泛型方法&lt;T&gt;foo(你可以使用E 代替T,但这不会改变任何东西 - - 它仍然是一个具有自己类型参数的泛型方法)。

    现在C&lt;String&gt; 也有&lt;Integer&gt;foo 等,由interface I 指定。

    如果这不是您需要的东西,那么您可能希望将interface I&lt;E&gt; 改为通用:

    interface I<E> {
        void foo(E e);
    }
    
    class C<E> implements I<E> {
        @Override public void foo(E e) {
        }
    }
    

    现在类型和方法共享相同的类型参数(例如,I&lt;Boolean&gt; 仅具有 foo(Boolean)C&lt;String&gt; 仅具有 foo(String))这很可能是您最初的意图。

    【讨论】:

    • 问题不在于@Override 注释。您告诉他要做的是通过在方法上引入另一个具有相同名称的参数来隐藏类中的泛型参数。 (Eclipse给出“类型参数E隐藏了类型E”)
    • 从历史上看,我总是写 @Override 而不是“覆盖”来提醒自己(和其他人)始终使用注释。我意识到这会引起混乱。现在应该更清楚了。你是对的隐藏部分;我个人认为这没什么大不了的。
    • 使用T而不是E作为方法会更好,但整个“设计”仍然感觉不对。
    • 名称隐藏在适当的上下文中很好,例如this.kommune = kommune; 在上面的代码中。我更喜欢将名称隐藏在这里而不是引入新的类型参数T 的原因是因为两者具有相同的确切界限。如果不是这种情况,那么当然最好是一个不隐藏不同类型的既定名称的新名称。
    • 好的,我采纳了大家的建议,去更笼统地讨论这个问题,并讨论了两种可能的解决方案之间的区别。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多