参考:
http://blog.csdn.net/zero__007/article/details/52245475
文章没转过来,下面是个人结论:
协变:子类的方法返回值可以是父类的子类型。但是方法形参不能是父类方法形参的子类型。
逆变:协变的反类型。
协变的应用是多态和里氏替换原则。
List<Number>并不是List<Integer>的父类型,两者没有任何关系。
List<Number> d = new ArrayList<Number>();//可以添加Number及Number的任何子类元素
List<? extends Number> d2 = new ArrayList<Integer>();//? extends规定了上限。不能添加除了null之外的任何元素。因为d2具体应用的list对象可以是Integer也可以是Float、Double等,所以不能添加确定的Number元素。
List<? super Number> d3 = new ArrayList<Number或者Object>();//? super规定了下限,可以添加Number的任何子类,但是不能添加Number的父类。
所以d2和d3中能不能添加元素、添加什么类型的元素关键看和后面具体的集合对象有没有可能存在冲突,如果存在冲突那肯定就不能添加元素了。
什么时候用? extends,什么时候用? super,有个PECS法则,即producer extends, consumer super。在一个集合是数据提供方就用? extends,是数据消费方就用? super。如代码:
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
int srcSize = src.size();
if (srcSize > dest.size())
throw new IndexOutOfBoundsException("Source does not fit in dest");
if (srcSize < COPY_THRESHOLD ||
(src instanceof RandomAccess && dest instanceof RandomAccess)) {
for (int i=0; i<srcSize; i++)
dest.set(i, src.get(i));
} else {
ListIterator<? super T> di=dest.listIterator();
ListIterator<? extends T> si=src.listIterator();
for (int i=0; i<srcSize; i++) {
di.next();
di.set(si.next());
}
}
}
这也就告诉我们,在自定义带list泛型方法的时候,如果这个方法是要消费形参传入进来的数据,这时形参是数据提供方,要定义成list<? extends T> src,如果方法是要产生数据并传给形参,这时形参是数据消费方,要定义成list<? super T> dst。
分享到:
相关推荐
协变(Covariance)和逆变(Contravariance)在编程语言理论中是非常重要的概念,它们涉及到接口和委托的类型兼容性。在.NET 5中,协变返回类型主要体现在泛型接口和委托上,使得代码能够更加安全地处理不同类型的...
这意味着Java的泛型不支持协变和逆变,但可以通过通配符(如?)来放宽类型限制。Java的泛型约束则相对较少,主要通过extends和super关键字来限制类型参数的范围。 在实际应用中,C#的泛型在效率上可能具有优势,...
6. **可变与不可变对象**:强调不可变对象的优势,如安全性、线程安全和共享性,并提供了构建不可变对象的指南。 7. **重写equals()和hashCode()**:遵循`equals()`和`hashCode()`合同,确保一致性并避免常见的陷阱...
5. **协变与逆变**: 在泛型中,类型参数的使用会影响类型安全。比如,List是List的子类型(协变),而List不是List的子类型(逆变)。 在实际开发中,理解和熟练运用Java集合框架和泛型能够大大提高代码的可维护性...
8. 字符串不可变:在C#和Java中,字符串都是不可变类型,一旦创建就不能更改。 9. 不可扩展的类:都有密封类(C#)或最终类(Java),禁止其他类继承。 10. 异常处理:两者都使用try-catch-finally语句块进行异常...
3. **泛型的协变与逆变**:在Java中,泛型默认是不变的,即`List<String>`不能赋值给`List<Object>`。但是,通过使用通配符,我们可以实现协变(如`List<? extends Number>`可以赋值给`List<Number>`)和逆变(如`...
- **不可变对象**: String类是不可变的,确保了字符串操作的安全性。 - **克隆(Clone)**: Java中的克隆是一个复杂的过程,涉及到对象状态的复制。 ### 数据类型深入 - **NaN**: 不是一个数字(Not a Number)的值...
- 字符串在Java和C#中都是不可变的引用类型,但在C#中,字符串可以使用字符串连接操作符,而在Java中则需要使用StringBuilder或StringBuffer来高效地拼接。 2. 面向对象特性: - C#支持接口和抽象类,两者都可以...
- **String**:不可变的字符序列。 - **StringBuffer/StringBuilder**:可变的字符序列,StringBuilder 在单线程环境中比 StringBuffer 性能更好。 #### 四、泛型 泛型允许你编写类型安全的通用代码。理解泛型接口...
super SomeClass`表示任何`SomeClass`或其父类的对象,但Java中不支持下界通配符的写法,一般通过协变(covariance)和逆变(contravariance)实现类似功能。 6. 类型擦除 - Java的泛型是通过类型擦除实现的,这...
此外,泛型的边界和协变、逆变问题也是类型安全的一大挑战,开发者需要理解其原理并合理运用。 其次,性能优化是Java开发者常常面临的挑战。虽然Java有JVM(Java虚拟机)进行自动内存管理和垃圾回收,但在高并发、...
例如,Guava库中的`ImmutableList.Builder`就利用了泛型缓存,实现了高效构建不可变列表的功能。 总的来说,泛型是现代Java编程的关键组成部分,它提高了代码的类型安全性,降低了出错的可能性,并且通过泛型缓存等...
- **泛型**:Java 和 C# 都支持泛型,但 C# 的泛型约束更强大,允许协变和逆变。 - **集合初始化器**:C# 提供了集合初始化器,使初始化集合变得更简洁,Java 通常需要使用 `add` 方法逐个添加元素。 总的来说,...
但是,C#引入了结构(struct),它是一种轻量级的引用类型,用于创建不可变类型或小对象。 - **自动装箱与拆箱**:C#中,值类型可以自动转换为对应的引用类型,反之亦然,这在Java中是不允许的。 2. **类与对象** ...
它支持协变、逆变等高级特性,但在使用中需要注意类型擦除带来的影响,并通过通配符、类型边界等方法解决泛型在使用中可能遇到的问题。 类型擦除(Type Erasure)是Java泛型的一种机制,它在运行时移除泛型信息,以...
- **不可变集合**:`Collections.unmodifiable*()`方法可以创建只读集合,增强安全性。 9. **并发编程** - **线程安全**:了解并发中的`volatile`, `synchronized`, `Atomic*`类等概念,确保数据一致性。 - **...
6. **协变与逆变:**Java泛型支持协变和逆变,这是在处理泛型接口和类时的重要特性。协变允许子类型替换父类型,而逆变则允许父类型替换子类型。在并发编程中,了解这些概念有助于编写更灵活和安全的代码。 7. **...
《C# 4.0:完整参考》是Herbert Schildt所著的一部全面而深入探讨C# 4.0编程语言的书籍。...此外,本书由领域内两位重量级专家共同完成,确保了内容的专业性和实用性,是学习C# 4.0不可或缺的资源之一。
泛型是Java编程语言中的一个核心特性,它在2004年的Java 5...总的来说,泛型是Java编程中不可或缺的一部分,它提高了代码的可读性、安全性和可维护性。理解并熟练运用泛型,能够帮助开发者编写出更高质量的Java程序。