Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/codeigniter/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 当类C用方法If(Ix)实现I时,为什么在返回类型中有自由,而在参数中没有自由?_Java_Types_Interface - Fatal编程技术网

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
    的参数朝着子类型化的方向移动(专门化它)时,函数也朝着这个方向移动,而结果类型必须朝着相反的方向移动(得到一般化)才能将函数子类型化。读者可能会画出来,看看它是如何与物理学中向量方差的定义相一致的
  • 这当然不是真正的证据,只是挥手而已。我没有真正的证据。别提这个问题

    Java中的子类化与类型的特定“子类型化”偏序一致。 著名的是定义子类型关系的一种方法。这一特定定义被称为“行为”亚型,它打开了一个无法确定的深兔子洞,这可能是我们关心的,也可能不是。(所以我们又有一个问题。)在这里,我们必须相信Java在行为亚型的表现上是忠实的。(习惯性地质疑它。一种可能的方法是仔细观察。)

    因此,让我们假设继承与Java中的子类型兼容。它给了我们什么

    具有子类型参数的函数不是原始函数的子类型。 也就是说,子类型项的方法不是原始项的子类型

    假设你有这个