什么';在Java中,它相当于C#IEnumerable?协变函数是有能力的,而不是可数函数
这种协方差在C#中是可能的: 从版本1开始,这两种语言都具有此功能。很高兴他们正在取得进展,使这成为现实。C#在语法上的摩擦较小 在所有OOP语言中,协变都是必须的,否则OOP继承将是一个无用的练习,例如什么';在Java中,它相当于C#IEnumerable?协变函数是有能力的,而不是可数函数,c#,java,C#,Java,这种协方差在C#中是可能的: 从版本1开始,这两种语言都具有此功能。很高兴他们正在取得进展,使这成为现实。C#在语法上的摩擦较小 在所有OOP语言中,协变都是必须的,否则OOP继承将是一个无用的练习,例如 A x; B y = new B(); x = y; 这种能力也应该扩展到泛型 谢谢大家的回答和见解。现在得到了一个具有协变功能的Java泛型的可重用方法。这不是我们中的一些人想要的语法,而是Java中的()语法。你必须用老方法来做,就像C#不支持泛型中的协变一样 然而,在Jav
A x;
B y = new B();
x = y;
这种能力也应该扩展到泛型
谢谢大家的回答和见解。现在得到了一个具有协变功能的Java泛型的可重用方法。这不是我们中的一些人想要的语法,而是Java中的(
)语法。你必须用老方法来做,就像C#不支持泛型中的协变一样
然而,在Java中,您可以假设泛型iterable是任何东西的iterable,用问号表示。任何东西的列表只包含对象
Iterable<A> a = new ArrayList<A>();
Iterable<?> b = a;
Iterable a=新的ArrayList();
可数b=a;
有一个很好的理由说明,在泛型集合中这种协方差是个坏主意:
假设你这样做:
ArrayList<A> a = new ArrayList<A>();
ArrayList<B> b = new ArrayList<B>();
a.add(new A());
b = a;
B item=b.get(0);
ArrayList a=新的ArrayList();
ArrayList b=新的ArrayList();
a、 添加(新的a());
b=a;
B项=B.get(0);
哇-一个只返回类型B的对象的函数返回了类型a。这显然不能工作。因此Java编译器为了确保类型安全而不允许它
不过这没什么大不了的-简单的解决方法是只使用非泛型集合类或将泛型类型参数限制为公共超类(在本例中为a)。Java泛型仅在通过通配符显式声明时才允许协变,以提供更严格的类型安全性。这是有效的:
Iterable<? extends A> a = new ArrayList<A>();
Iterable<B> b = new ArrayList<B>();
a = b;
iterableconomicance不是一个坏主意。它遵循了SOLID原则中的Liskov替换原则。B扩展了a。a至少可以/应该能够保存对B的引用。当然,任何静态类型的语言都可以阻止将a赋值给B,即B=a;
是而且不应该被允许的。您的示例有点做作,但是y静态类型语言将不允许这样做,我考虑的是a=b;
,而不是b=a;
.C#4至少简化了Liskov替换原则,将b分配给a是可以的,也就是说,这在C中是允许的:a=b;
@Hao Java语言设计者不想允许这样做,因为它不是类型安全的。(参见MichaelBorgwardt关于ArrayStoreException的评论)。它不是类型安全的,因为它们不是子类。提供的解决方案是使用通配符。我不确定C#如何解决这个问题,或者它是否也抛出了一些与ArrayStoreException等价的东西(这意味着它不是类型安全的).@Puce Java语言设计人员允许它。这将编译:A[]A=new B[10];
并且没有运行时错误,没有引发异常,并且他们也将此功能扩展到泛型。但是您不能将集合的元素(B)更改为其超类(A)和不兼容类型的对象;例如,这是不允许的(将编译):a[0]=new a();
,它将导致ArrayStoreException
。如果B有一个子类,比如说C(class C扩展了B
),这是允许的:a[0]=new C();
,这是安全的,没有运行时错误,没有引发异常。这也是安全的:a[0]。setFirstname(“James”)
@Hao再说一次,Java只允许对数组使用此选项,但不允许对泛型类型使用此选项,因为它不是类型安全的。(我不知道为什么允许对数组使用此选项,但现在无法更改。)如果不使用类的原始版本,Java泛型永远不会抛出ArrayStoreException或ClassCastException。只有数组抛出ArrayStoreException。@现在在Java中允许使用Puce泛型协方差(请参阅@MichaelBorgwardt-answer),并且它是类型安全的;例如,它不允许您调用有可能操纵集合的方法(例如add).Java generic convarance的语法与C#语法相比并不自然。无论如何,我现在只使用我们现有的语法,正如@MichaelBorgwardt所建议的,这就足够了IterableIn在您的数组示例中,问题是它降低了编译时类型的安全性-一些操作(请参阅对mikera答案的评论)在编译时允许,但在运行时失败(爪哇中的代码> ARRAYStureExtExabor),显然,java泛型的设计者认为编译时类型安全比协方差更重要(并且隐含地必须考虑数组协方差为设计错误)。@MichaelBorgwardt这不是一个设计错误,事实上,当类型是简单的(一个变量)和数组(请参阅我编辑的问题)时,Java可以看到超类和子类之间的协方差,Java编译的数组版本和它的单变量版本一样好。在运行时没有ArrayStoreException
,我刚刚运行了它。只要接收赋值的变量是超类,就不会有任何ArrayStoreException
添加行x[0]=new A()
到您的数组代码,肯定会出现数组存储异常
@MichaelBorgwardt啊..现在我知道:-)谢谢您指出这一点out@MichaelBorgwardt嗯……基本上,协方差(可以是数组,也可以是泛型),必须用于只读活动。现在,我看到了Java不允许调用来自
import java.util.*;
public class Covariance2 {
public static void testList(Iterable<? extends A> men) {
for(A good : men) {
System.out.println("Good : " + good.name);
}
}
public static void main(String[] args) {
System.out.println("The A");
{
List<A> team = new ArrayList<A>();
{ A player = new A(); player.name = "John"; team.add(player); }
{ A player = new A(); player.name = "Paul"; team.add(player); }
testList(team);
}
System.out.println("The B");
{
List<B> bee = new ArrayList<B>();
{ B good = new B(); good.name = "George"; bee.add(good); }
{ B good = new B(); good.name = "Ringo"; bee.add(good); }
testList(bee);
}
}
}
class A { String name; }
class B extends A {}
The A
Good : John
Good : Paul
The B
Good : George
Good : Ringo
using System.Collections.Generic;
using System.Linq;
public class Covariance2 {
internal static void TestList(IEnumerable<A> men) {
foreach(A good in men) {
System.Console.WriteLine("Good : " + good.name);
}
}
public static void Main(string[] args) {
System.Console.WriteLine("The A");
{
IList<A> team = new List<A>();
{ A player = new A(); player.name = "John"; team.Add(player); }
{ A player = new A(); player.name = "Paul"; team.Add(player); }
TestList(team);
}
System.Console.WriteLine("The A");
{
IList<B> bee = new List<B>();
{ B good = new B(); good.name = "George"; bee.Add(good); }
{ B good = new B(); good.name = "Ringo"; bee.Add(good); }
TestList(bee);
}
}
}
class A { internal string name; }
class B : A {}
Iterable<A> a = new ArrayList<A>();
Iterable<?> b = a;
ArrayList<A> a = new ArrayList<A>();
ArrayList<B> b = new ArrayList<B>();
a.add(new A());
b = a;
B item=b.get(0);
Iterable<? extends A> a = new ArrayList<A>();
Iterable<B> b = new ArrayList<B>();
a = b;