【问题标题】:Java Generics and Inheritance (specific issue)Java 泛型和继承(具体问题)
【发布时间】:2015-08-01 18:56:14
【问题描述】:

我想继承泛型类和接口。有一个问题我无法理解。问题体现在下面的简化代码中:

public interface A1 <T extends F1<?>> {
    public void compare(T f1, T f2);
}

public class F1 < T extends A1<? extends F1<T>> >
{
    T a;
    public void compare(F1<T> f) {
        a.compare(this, f);
    }
}

A1A1.javaF1F1.java

我得到编译时错误:

The method compare(capture#1-of ? extends F1<T>, capture#1-of ? extends F1<T>) 
in the type A1<capture#1-of ? extends F1<T>> is not applicable for the 
arguments (F1<T>, F1<T>)

我需要 A1 &lt; ? extends F1&lt; T&gt;&gt; 用于继承 F1 的类。

附加信息(编辑后):

我想要 2 个层次结构(“O”而不是“F”,“C”而不是“A”):

  • 类的第一个层次结构(O1O2 ...)包含对象的所有相关信息以及“比较器”(分析一对相同类型的两个对象的任何函数)。对象的类定义了它接受的比较器的类型,主要要求是比较器应该接受托管对象类型的 2 个对象。例如,O1 只能接受所有比较 O1 的比较器,O2 可以有接受 O2 的比较器。
  • 类的第二个层次是比较器(C1C2...)本身。他们比较层次结构 1 中的对象。

两个层次结构中的每个元素都添加了在子类中使用的补充功能。

让我们考虑以下用例,这是通过这些层次结构的路径之一:

  • O1 有 2 个成员(不是类型!):val(任何类型),cmp(类型为 C1,只接受 O1)。
  • C1_1 没有对应的对象O,但它实现了与集合相关的附加功能。它可以比较集合中的对象(“基本”类型,如intStringTColor 和“复杂”对象O)并应用仅返回 1 个值(例如最大值)的聚合器。 如果集合是基本类型,则使用cmp2 来比较集合中的元素。对于集合中的复杂对象,使用它们的集成比较器。
  • C1_1_1 实现了如何比较对象列表 (1:1)。 C1_1_2(未显示)可用于比较对象集,例如与叉积。
  • O1_1 具有 TVALB 类型的 val,其中 extend List&lt;TVALA&gt; 和比较器 C1_1_1

C1_1_1/2 的其他子类可以有更精细的实现来比较基本对象和复杂对象。

支持代码:

public abstract class O1 <TVAL, TC extends C1<? extends O1<TVAL>>>
{
    TVAL val;
    TC cmp;
    public double compare(O1<TVAL, TC> o) {
        return cmp.compare(this, o);
    }
}
public interface C1 <TO extends O1<?, ?> > {
    public double compare(TO t1, TO t2) {
      // ...
    }
}

public abstract class C1_1 <
        TVALA,
        TVALB extends Collection<TVALA>,
        TO extends O1<TVALB, ?>
    >
    extends C1 <TO> {

    Cmp2<TVALA, Double> cmp2;
    IAggregator<TVALA> aggr;

    Double compare(TO o1, TO o2) {
        return aggr.apply(
            compPairs(o1.val, o2.val, cmp2)
        );
    }

    abstract pairs<TVALA> compPairs(
        TVALB b1
        , TVALB b2
        , Cmp2<TVALA> cmp2);

    // aggregation
    public static class Aggr1<TVALA> implements IAggregator<TVALA> {
        public Double apply(pairs<TVALA> pairs) {
            //...
        }
    }
    public static class Aggr2<TVALA> implements IAggregator<TVALA> {
        // ...
    }
}

public abstract class C1_1_1 <
        TVALA,
        TVALB extends List<TVALA>,
        TO extends O1<TVALB, ?>
    >
    extends C1_1 <TO> {
    pairs<TVALA> compPairs(
            TVALB b1
            , TVALB b2
            , Cmp2<TVALA> cmp2) {
        // ...
    }
}

public abstract class O1_1 <
    TVALA extends O1<?,?>,
    TVALB extends List<TVALA>
    , TC extends C1_1_1<TVALA, TVALB, ? extends O1_1<TVALA, TVALB, TC>>>
    extends O1<TVAL>
{
    // ...
}

我使用依赖注入在特定类中分配比较器(C-tree 到对象O)。因此,这种方法(如果可行的话)可以灵活地为特定类型的对象选择合适的比较器。

【问题讨论】:

  • 为了澄清,我们假设 F1 是特定“对象”的泛型类型,A1 是特定比较器的泛型类型,用于比较相同类型的 2 个特定对象。跨度>
  • 鉴于过去一小时有 3 个答案,您可能应该更详细地说明您正在尝试做的事情 - 显然,您发现没有一个有用。
  • 如果您使用比A1F1 更具描述性的名称也会有所帮助。你在比较什么?
  • @bcorso 我添加了更多细节。
  • 我的回答哪里不足? (除了多态的东西,这本质上是不可避免的。)。请注意,泛型不是相互递归的,因此尚不清楚剩余的复杂性是什么......

标签: java generics inheritance


【解决方案1】:

您可以删除您的A1 类,只使用Comparable 接口。

public class F1<T extends Comparable<T>> implements Comparable<F1<T>> {
    T t;

    @Override public int compareTo(F1<T> other){
        return t.compareTo(other.t);
    }
}

【讨论】:

  • @OliverCharlesworth 这就是我写a.compareTo(f.a) 的原因,其中f.aT。最后他试图比较T的,我认为他只是过度设计了一点。
  • 啊,是的。但这不是期望的行为。 AFAICS,目的是比较F1 实例。
  • 我已经更新了我的答案。基本上,我是说他过度设计了这个问题,比较 T 或比较 F1 应该没有那么困难。
  • 我认为他希望能够注入特定的比较行为。如果我正确理解了用例,那么我认为我的答案中的代码可以完成工作(尽管F1 可能应该实现Comparable...)
  • 这并不反映拥有 2 个层次结构树的想法。泛型类型的第一棵树是对象 F1(泛型),F2(泛型,F1 的子类)等,第二棵树是比较器:A1(泛型,比较 F1 的),A2(泛型,A1 的子类,比较 F2 的) .
【解决方案2】:

让我们先重命名类型变量,因为两个 T 代表不同的东西,希望它能让事情更清楚:

public interface A1 <T extends F1<?>> {
    public void compare(T t1, T t2);
}

public class F1 < S extends A1<T extends F1<S>> >
{
    S s;
    public void compare(F1<S> f) {
        s.compare(this, f);
    }
}

现在您看到您实际上是在s 上调用compare,它是A1&lt;T&gt; 的一个实例,其中T 可以是F1&lt;S&gt; 的子类。但是,您传递的参数的类型为F1&lt;S&gt;,而不是子类T,这几乎就是编译器所说的。

本质上,您使方法参数是协变的,而它们可能应该是逆变的。为此,请使用super 而不是extends

public class F1 < S extends A1<? super F1<S>> >
{
    S s;
    public void compare(F1<S> f) {
        s.compare(this, f);
    }
}

【讨论】:

  • 除了不是S extends A1&lt;T extends F1&lt;S&gt;&gt;,是S extends A1&lt;? extends F1&lt;S&gt;&gt;。那么,OP 正在有效地尝试调用 A1&lt;?&gt;.compare() 不是真正的问题吗?
  • 如果我写类 F1 > >,那么我不能有 F2 的子类,以及 A1 的 A2。例如:public interface A2 &lt;T extends F2&lt;?&gt;&gt; extends A1&lt;T&gt; {...}public class F2 &lt;S extends A2&lt;F2&lt;S&gt;&gt;&gt; extends F1&lt;S&gt; {...}。 F2 的错误是 Bound mismatch: The type S is not a valid substitute for the bounded parameter &lt;S extends A1&lt;F1&lt;S&gt;&gt;&gt; of the type F1&lt;S&gt;
  • F1 中的 super 对我不起作用。我想继承我之前评论中提到的 F1 和 A1。
  • 你将无法拥有它。如果可能的话,考虑在继承的F2.compare 方法中传递给A2.compare 的类型。第一个参数的类型为F2&lt;S&gt;,第二个参数的类型为F1&lt;S&gt;。但是A2.compare 应该只接受 F2 类型...
  • @OliverCharlesworth 基本上是的,? extends F1&lt;S&gt; 表示F1&lt;S&gt; 的未知子类型,这不是一个有用的东西。无论如何&lt;S extends A1&lt;T extends F1&lt;S&gt;&gt;&gt; 是为了说明一个观点而编造的语法,你不能将类型变量 T 放在那个位置。
【解决方案3】:

问题在于a 的类型为A1&lt;? extends F1&lt;T&gt;&gt;,即由通配符参数化。然后就不能调用A1&lt;?&gt;.compare,因为编译器不知道? 代表什么具体类型。

对于这个更简单的代码,您会看到相同的行为:

A1<? extends Object> a = ...;
a.compare(new Object(), new Object());

如果我正确理解您的用例,您只希望能够在特定比较器上参数化 F1。所以看起来你应该能够做到这一点:

interface A1<A extends A1<A>> {
    public void compare(F1<A> f1, F1<A> f2);
}

class F1<A extends A1<A>> implements Comparable<F1<A>> {
    A a;
    @Override
    public void compareTo(F1<A> f) {
        a.compare(this, f);
    }
}

请注意,我已经让 F1 实现了 Comparable,因为这是很好的做法。

【讨论】:

  • 这似乎是一个很好的解决方案。谢谢!唯一的事情,compareTo 函数接受所有不同的对象,它们是 F1 的子类。但这对我来说不是什么大问题。
  • 这个递归定义是什么意思? A1&lt;A extends A1&lt;A&gt;&gt;如何解读?
  • @ruslancho:您对此无能为力。多态性的本质意味着当需要超类时,总是可以替换子类的实例。
  • @ruslancho:就递归泛型而言,请参阅stackoverflow.com/questions/211143/java-enum-definition。基本思想是唯一满足界限的东西是从它继承的东西(所以想象你做了class A2 implements A1&lt;A2&gt;;这是有效的,因为A2确实扩展了A1&lt;A2&gt;)。
  • @OliverCharlesworth:“基本思想是唯一满足界限的东西是从它继承的东西”注意class A3 implements A1&lt;A2&gt;也是有效的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-06
  • 1970-01-01
  • 1970-01-01
  • 2011-08-20
  • 1970-01-01
相关资源
最近更新 更多