更改参数参数涉及的泛型
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 源和目标。您想告诉编译器 src
和 dest
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>
,它就是。
目标: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 源和目标。您想告诉编译器 src
和 dest
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>
,它就是。