更改参数参数涉及的泛型

Changing argument parameters Generics involved

目标:dst = src in main class

我尝试了各种组合来将源参数复制到目标参数中,但找不到有效的组合。如何解决?

public class Bounds {
    public static <T extends Number> void copy(Paar<? extends Number> src, Paar<? extends Number> dst) {
        // src = (1, 2) dst = (1.1, 2.2)
        T c1 = (T) src.p1;
        T c2 = (T) src.p2;

        dst.p1 = c1;
//Error: 'Type mismatch: cannot convert from T to capture#3-of ? extends Number'
        dst.p2 = c2;

        System.out.println("Src:" + src);
        System.out.println("Dst:" + dst);
    }
}
public class BoundsMain {

    public static void main(String[] args) {

        Integer[] iFeld = { 1, 2, 3 };
        Number[] nFeld = { 1.1, 2.2, 3.3 };
        Paar<Integer> src = new Paar<>(iFeld[0], iFeld[1]);
        Paar<Number> dst = new Paar<>(nFeld[0], nFeld[1]);

        System.out.println("src=" + src + "\ndst vor copy =" + dst );
        // src = (1, 2) dst = (1.1, 2.2)

        Bounds.copy(src, dst);
        System.out.println("src nach copy =" + src);
        System.out.println("dst nach copy =" + dst);
        // src = (1, 2) dst = (1.1, 2.2)
    }
}

为您的类型参数考虑多个有效 'values',然后考虑该方法对您想到的所有值的含义可能会有所帮助。如果您发现至少一个没有意义 - 您现在明白为什么 javac 不会编译您的代码。

您写道:

<T extends Number> void copy(Paar<? extends Number> src, Paar<? extends Number> dst)

此方法签名有 3 个完全不相关的类型变量:

  • T,这是完全未使用的,因此毫无意义,是可指定的(如果需要,调用者可以明确地选择 T 的值,如果他们不这样做,系统将推断它),并且有一个名称,因此您可以在多个地方使用它。您在零个地方使用它(转换为泛型不算数,它不生成代码,不检查任何内容,也不转换任何内容。它主要只是发出警告)。
  • 第一次使用? extends Number。在泛型术语中,? 等同于:组成一个新变量,给它这个界限,在这里而不是其他任何地方使用变量,并使它成为 non-specifiable.
  • 第二次使用? extends Number.

鉴于所有 3 个完全不相关,您的代码不会在没有疯狂警告的情况下编译,并且不会为您提供您想要的类型安全:将第一个参数转换为 T 是 non-sensical(也许3 个中的第一个是 Integer,第二个是 Character - 这些类型不兼容!)


快捷边栏:

此处有少数 report-style 替代解释:您的意图是 copy 方法将一种数字类型转换为另一种 - 您打算因为可以将 Paar<Byte> 作为源和 Paar<Double> 作为目标传递,并且复制代码会将源中的字节转换为双精度,然后将它们分配给 paar。我可以看出这些 完全不相关的任务 (类型强制或断言,与原始转换)在你的大脑中是如何被混淆为某种相似的。它们不是——它们完全无关。但是在 java 中,它们恰好使用了完全相同的语法('cast' 操作:(SomeType) someExpr)。别搞错了。这与英语中 arm 表示 body 部分和给某人一把枪的行为。无关。完全。我假设这 不是 你想要的。


让我们解决您的问题

您只想 link 源和目标。您想告诉编译器 srcdest Paar 上的类型变量之间必须存在某种关系,这样将值从一个复制到另一个就不会出错。

为此,我们显然只需要一个参数,首先:

public static <T extends Number> void copy(Paar<T> src, Paar<T> dst) {

现在它已经可以正常编译了,尽管您的 main 代码仍然无法运行,我们稍后会解决这个问题。上面的代码说:有一些类型,我们称它为 T 但我们不知道它是什么。我们确实限制了它:它要么是 Number,要么是 Number 的某个子类型。这种方法几乎有上百万个版本——一个用于 Number,一个用于它的每个子类型(假设 Number 不是 'sealed',即任何 body 都可以写extends Number 的代码,当然,理论上它是无限的。

要点是,只有一种类型 var,因此调用者可以选择他们想要的任何类型,只要它是 Number 或其子类型即可,但是,他们只能选择一种类型 - both Paar objects 都有那个作为他们的 T。因此,你只是 cannot使用例如调用此方法一个 Paar<Double> 和一个 Paar<Integer> - 在这一点上不能为 T 选择任何东西(泛型是 不变的 ,意思是 Number 不会在这里工作。只有确切的类型才行)。

但这并不完全,对吧?有不同的类型仍然可以 'work' - 具体来说,如果源 Paar 的 T 是目标 Paar 的 T 的子类型,那么 确实 工作,如你的例子。所以我们需要一些细微的调整。随便选一个,没关系:

<T extends Number> void copy(Paar<T> src, Paar<? super T> dst)

<T extends Number> void copy(Paar<? extends T> src, Paar<T> dst)

两者都有效。这是说:

来电者可以选择一种类型。任何类型,只要它是 Number 或其子类型。来电者挂机后将执行以下操作:

第一个版本:

  • 对于第一个 arg 传递一个 Paar object,其 T 完全 与为此方法调用选择的 T 匹配。
  • 对于第二个 arg,传递一个 Paar object,其 T 与为此方法调用选择的 T 相同, 它的任何超类型。

对于第二个版本:

  • 对于第一个 arg 传递 Paar object 其 T 是 恰好 为该方法调用选择的 T,或者任何T 的子类型。
  • 对于第二个参数,传递 Paar object 其 T 完全匹配。

好的是,通常调用者不必显式选择 T,编译器会推断出 'works'。他们可以明确选择如果他们愿意的话。

现在您的代码可以运行了 - 如果您采用第一个想法,那么 Bounds.copy(src, dst) 将意味着编译器为 T 选择 Integer,然后调用运行:第一个 arg 必须完全是Paar<Integer>(它是),第二个参数必须是 Paar<X>,其中 X 是 Integer 或其超类型之一。它是 - Number 是 Integer 的超类型。因此,通话有效。请注意,您需要 no 强制转换为 (T) 复制代码中的任何位置!

在第二种情况下它也有效——编译器将选择 Number,然后检查第一个参数是否确实是 Paar<Y>,其中 Y 是数字或子类型(它是;整数是 Number 的子类型),如果第二个 arg 正好是 Paar<Number>,它就是。