文档章节

Java泛型总结

281165273
 281165273
发布于 2013/08/21 15:02
字数 1549
阅读 81
收藏 0
1.泛型概念

泛型即参数化类型,主要作用是为了创建更加范化的代码和提供编译期类型安全。

最常见的理据就是不用等到运行期,编译期就可以确保容器类不会装入不同类型的元素,并且可以不用强制类型转换。

例子:

List<String> list=new ArrayList<String>();
list.add("ok");
list.add(Integer.valueOf("11"));//compile error
String s=list.iterator().next();
2.泛型声明

例子:

public interface List<E> {
    void add(E x);
    Iterator<E> iterator();
}

public interface Iterator<E> {
    E next();
    boolean hasNext();
}

尖括号部分形式类型参数的声明,类型参数在整个类的声明中可用,几乎是所有可是使用其他普通类型的地方。

一个泛型类型的声明只被编译一次,并且得到一个class文件,就像普通的class或者interface的声明一样。

类型参数就跟在方法或构造函数中普通的参数一样。就像一个方法有形式参数(formal value parameters)来描述它操作的参数的种类一样,一个泛型声明也有形式类型参数(formal type parameters)。当一个方法被调用,实参(actual arguments)替换形参,方法体被执行。当一个泛型声明被调用,实际类型参数(actual type arguments)取代形式类型参数。

3.泛型与继承

即泛型没有协变性

例子:

List<String> list1=new ArrayList<String>();
List<Object> list2=list1;//compile error
list2.add(1);

假设允许第2行代码,那么第三行代码将可以在一个String的集合中插入一个数字。

4.通配符

当一个方法可以处理任意类型的集合,而不关心集合中元素的类型时,使用无界通配符,collection<?>相当于各种collection的父类。

旧代码:

void printCollection(Collection c) {
       Iterator i = c.iterator();
           for (int k = 0; k < c.size(); k++) {
                  System.out.println(i.next());
         }
}

新代码:

void printCollection(Collection<Object> c) {
           for (Object e : c) {
                 System.out.println(e);
           }
}

这个方法可以处理任意集合,转换成泛型如果参数使用collection<Object>,那么方法的作用就小了,原来可以打印任意类型集合,现在只能打印collection<Object>集合了。

正确的泛型代码:

void printCollection(Collection<?> c) {
for (Object e : c) {
System.out.println(e);
}
}

但是注意,如果方法关心集合元素的类型,则需要使用有界通配符了,如下面代码:

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // compile error

因为我们不知道collection里元素的类型,所有不能添加任意元素到集合,add方法有类型参数E作为集合的元素类型。我们传给add的任何参数都必须是一个未知类型的子类。因为我们不知道那是什么类型,所以我们无法传任何东西进去。唯一的例外是null,它是所有类型的成员。

另一方面,我们可以调用get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个Object,因此把get的返回值赋值给一个Object类型的对象或者放在任何希望是Object类型的地方是安全的。

5.有界通配符

例子:&#160;

public abstract class Shape {

public abstract void draw(Canvas c);

}

public class Circle extends Shape {

private int    x, y, radius;

public void draw(Canvas c) { // ...

}

}

public class Rectangle extends Shape {

private int    x, y, width, height;

public void draw(Canvas c) {

// ...

}

}

public class Canvas {

public void draw(Shape s) {

s.draw(this);

}

public void drawAll(List<Shape> shapes) {

          for (Shape s : shapes) {

             s.draw(this);

         }

}}

&#160;

现在,类型规则导致drawAll()只能使用Shape的list来调用。它不能,比如说对List<Circle>来调用。这很不幸,因为这个方法所作的只是从这个list读取shape,因此它应该也能对List<Circle>调用。我们真正要的是这个方法能够接受一个任意种类的shape:

public void drawAll(List<? extends Shape> shapes) { //..}

这里有一处很小但是很重要的不同:我们把类型 List<Shape> 替换成了 List<? extends Shape>。现在drawAll()可以接受任何Shape的子类的List,所以我们可以对List<Circle>进行调用。

List<? extends Shape>是有限制通配符的一个例子。这里?代表一个未知的类型,就像我们前面看到的通配符一样。但是,在这里,我们知道这个未知的类型实际上是Shape的一个子类(它可以是Shape本身或者Shape的子类而不必是extends自Shape)。我们说Shape是这个通配符的上限(upper bound)。

像平常一样,要得到使用通配符的灵活性有些代价。这个代价是,现在像shapes中写入是非法的。比如下面的代码是不允许的:

public void addRectangle(List<? extends Shape> shapes) {

shapes.add(0, new Rectangle()); // compile-time error!

}

你应该能够指出为什么上面的代码是不允许的。因为shapes.add的第二个参数类型是? extends Shape ——一个Shape未知的子类。因此我们不知道这个类型是什么,我们不知道它是不是Rectangle的父类;它可能是也可能不是一个父类,所以这里传递一个Rectangle不安全。

而正好相反的是下限通配符,? super TestObject——一个TestObject未知的超类,因此我们可以将TestObject及其子类安全的放入集合,而当我们从集合中取出元素时,我们无法知道究竟是哪个父类,因此赋值是不安全的。

public class TestObject {

}

public class TestObject1 extends TestObject {

}

	

public void addTestObject(List<? super TestObject> list) {

          list.add(new TestObject1());

          TestObject to=list.get(0);//compile error

}

关于GET和SET原则

如果方法传入的泛型是生产者,即将实例加入到当前容器类,则用?extends,如果是从当前容器类消费实例,放入参数传入的容器类,则用?super

关于自限定

Enum<E extends Enum<E>>

用于限定继承关系A extends Enum<A>

并且会限制重载,方法的参数类型会协变,使得子类中不能即含有基类型参数的方法,又含有子类型参数的方法,而如果不是自限定的,则可以重载

泛型的重载还有一个特殊的地方,可以用方法返回值来确定重载的版本

© 著作权归作者所有

共有 人打赏支持
281165273
粉丝 4
博文 64
码字总数 35522
作品 0
武汉
程序员
Java语言学习(十一):枚举类型和泛型

Java中一个重要的类型:枚举,它可以用来表示一组取值范围固定的变量,使用 enum 关键字定义枚举类型,其中元素不能重复,通常大写表示。利用Java的反射机制,可以在运行时分析类,如查看枚举...

海岸线的曙光
07/18
0
0
Kotlin语言中的泛型设计哲学

Kotlin语言的泛型设计很有意思,但并不容易看懂。关于这个部分的官方文档,我反复看了好几次,终于弄明白Kotlin语言泛型设计的背后哲学。这篇文章将讲述Kotlin泛型设计的整个思考过程及其背后...

欧阳锋
04/16
0
0
【J2SE】JAVA语法糖之-伪泛型

JAVA语法糖之-伪泛型 泛型即参数化类型 首先看一个泛型例子: public static void main(String[] args) {List<String> strList = new ArrayList<String>();strList.add("generic test!");fo......

磊神Ray
2011/09/28
0
0
Java 8新特性探究(六)泛型的目标类型推断

简单理解泛型 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。通俗点将就是“类型的变量”。这种类型变量可以用在类、接口和方法的创建中。...

OSC闲人
2013/12/14
0
3
JAVA基础再回首(三十)——JAVA基础再回首完美结束,感概万千!

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m366917/article/details/52724939 JAVA基础再回首(三十)——JAVA基础再回首完美结束,感概万千! 经过了几...

Aduroidpc
2016/10/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

聊聊redisson的DelayedQueue

序 本文主要研究一下redisson的DelayedQueue maven <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.8.1</version></dependenc......

go4it
14分钟前
1
0
一张图看懂JVM

JVM结构示意图 JVM总体概述 JVM总体上是由类装载子系统(ClassLoader)、运行时数据区、执行引擎、内存回收这四个部分组成。其中我们最为关注的运行时数据区,也就是JVM的内存部分则是由方法...

小致dad
16分钟前
0
0
安全管理标准

安全生产严重等级分类: 故障频次: 风险等级矩阵:

乔老哥
47分钟前
2
0
数据结构“树”的相关微视频

今天在腾讯视频上闲逛,然後发现一个叫“岚人”的用户上传了几段小视频,基本上都在5分钟以内,讲解了关于树的一些结构和算法。零代码,非常适合初学者入门。不过,对于老鸟来说,这也是非常...

Iridium
59分钟前
1
0
10-利用思维导图梳理JavaSE-Java 集合

10-利用思维导图梳理JavaSE-Java 集合 主要内容 1.Collection接口 2.Set接口 2.1.Set接口概述 2.2.HashSet类 2.3.TreeSet类 2.4.SortedSet接口 3.List接口 3.1.List接口概述 3.2.ArrayList类...

飞鱼说编程
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部