Java 当类C用方法If(Ix)实现I时,为什么在返回类型中有自由,而在参数中没有自由?
考虑以下代码:Java 当类C用方法If(Ix)实现I时,为什么在返回类型中有自由,而在参数中没有自由?,java,types,interface,Java,Types,Interface,考虑以下代码: public interface I { I f(I x); } public class C1 implements I { public I f (I x) { return null; } } public class C2 implements I { public C2 f (I x) { return null; } } public class C3 implements I { public I f (C3 x) { return null; } }
public interface I { I f(I x); }
public class C1 implements I { public I f (I x) { return null; } }
public class C2 implements I { public C2 f (I x) { return null; } }
public class C3 implements I { public I f (C3 x) { return null; } }
public class C4 implements I { public C4 f (C4 x) { return null; } }
public class C5 implements I { public C1 f (I x) { return null; } }
如果您尝试编译,您将看到C3
和C4
失败:
C3 is not abstract and does not override abstract method f(I) in I
C4 is not abstract and does not override abstract method f(I) in I
因此我可以专门化返回类型,但参数必须保持抽象。换句话说
换句话说,我可以指定方法的返回类型(在定义中它甚至可能是C2
)
对于C1
-我可以混合),但我不能限制参数,因此f
在
输入
- 这种情况背后的逻辑是什么
- 为什么不允许专门化论点
- 为什么允许将返回类型专门化为
类型或保持其抽象性this
- 为什么允许将返回类型专门化为同一接口的另一个实现?
(如
)C5
p.S.关于协变回报率的另一个问题显然没有涵盖上述所有要点(如果有的话)
- 我不是问“什么”,而是问“为什么” 再次,我的问题是关于一个类与一个接口的关系,而不是一般的子类(即使我们考虑实现一个接口,一个子类的例子,它本身是一个开放的,但哲学的,问题)。
- 然而,这个问题再一次是一般性的,而我的问题是针对Java的
- 最后,措辞完全不同。我甚至不知道这个“协变”是从哪里来的。比如“向量”协变?“函子”协变?如果有深刻的类比,就必须加以解释
public interface I { I f(I x); }
I[] all = { new C1(), new C2(), new C3(), new C4(), new C5() };
I argument = availableArguments[Random.nextInt(all.length)];
I c1 = new C1();
I c2 = new C2();
I c3 = new C3();
I c4 = new C4();
I c5 = new C5();
public class C1 implements I { public I f (I x) { return null; } }
I result = c1.f(argument); // ok; argument is an I, an I is returned
public class C2 implements I { public C2 f (I x) { return null; } }
I result = c2.f(argument); // ok; argument is an I, returned C2 can be assigned to I
public class C3 implements I { public I f (C3 x) { return null; } }
I result = c3.f(argument); // would fail: argument is not of type C3 as required
public class C4 implements I { public C4 f (C4 x) { return null; } }
I result = c4.f(argument); // would fail: argument is not of type C4 as required
public class C5 implements I { public C1 f (I x) { return null; } }
I result = c5.f(argument); // ok; argument is an I, returned C1 can be assigned to I
故事在这里有点像 接口是一种类。 这一点并不明显,但Java“接口”的另一个合适名称是“抽象类”。接口实际上是类的一个子集这一点当然值得怀疑,所以我们应该单独提问。如果我们发现这是错误的,我们也应该重新审视这个答案。但现在,让我们假设这是真的 我们还必须假设Java“类”是一种类型。这一点在其他地方也可能受到质疑。如果我们假设这一点,我们可以从类型理论中得出一些见解。我们特别需要的是一个概念。简而言之,
C
是I
的一个子类型,这意味着当代码中有一个I
类型的变量时,可以用C
类型的变量替换它,并且代码的某些部分仍然有效
旁注:
- 当然,您永远无法实际创建类型为
的值,因为I
是一个抽象类-它没有定义;但是,您可以考虑一个已知的值,即只有一个类实现了<代码> i>代码>I
。
- 您的代码的哪一部分将在以后工作取决于子类型的确切定义,其中有一些-请继续阅读以了解Java使用的是哪个定义
f:Integer->Boolean
。它接受一个数字并返回一个真值。例如,让它告诉我们数字是否为偶数f--specialize argument-->f1:Prime->Boolean
。(3个还是假的,2个是真的。一切都好。)后者起作用的地方,前者也会起作用。因此,f1
是f
的一个子类f--generalizeresult-->f2=Integer->Byte
。(我们只需将True转换为0x01
,将False转换为0x00
),如果需要f2
,则可以使用f
。因此,f2
也是f
的一个子类李>
Prime
是Integer
的一个子类:您仍然可以对它们进行加法、乘法和求逆,但也可以Bool
是Byte
的一个子类:您仍然可以将其存储在内存中,但也可以将Bool
压缩数组存储在比Byte
数组小8倍的内存中f
的参数朝着子类型化的方向移动(专门化它)时,函数也朝着这个方向移动,而结果类型必须朝着相反的方向移动(得到一般化)才能将函数子类型化。读者可能会画出来,看看它是如何与物理学中向量方差的定义相一致的