概述
泛型
为了让集合集合记住其数据类型,jdk1.5开始,增加了泛型.增加了泛型的集合,可以记住集合中数据元素的类型.
java中泛型应用最多的地方是集合类。
1 定义数组和集合的比较
在定义数组时都需要指定元素的类型,例如:
String[] arr = new String[10];
对于arr而言,只能为元素指定String类型的值,而其他类型不行。
-------------------
我们以前定义的集合对象都是可以存储任意类型(Object)的对象的。就像是在使用Object类型的数组一样。但这有一个缺点,就是存入后再取出的元素需要强转。这也是可能发生ClassCastExcetpion的地方。
-------------------
大多数情况下,我们不会对同一个集合对象添加多种类型的元素,所以我们最希望可以像使用数组一样,在定义集合对象时指定可以装载的元素类型。
2 泛型的好处
将运行期遇到的问题转移到了编译期。
坏处也不少!!!
Java没有类模板的概念,为了添加这一概述,就出现了泛型。这也是为了兼容性迁移的第一步吧。泛型被称为四不像!这可能需要一个很慢长的迁移的过程,也许将来会完善的!相对C++而言,Java的泛型可能是败笔了。
3 使用泛型类
泛型类中使用的类型变量都被实际参数赋值。
使用泛型集合类
4 泛型类需要给类型变量赋值
类型参数是变量,你要给类型参数赋值。当然这个值必须是个类型。
定义泛型类一定要给类型参数赋值,不然就等同于赋值为Object。
在泛型类中使用的类型参数都会被赋的值替代。例如定义的属性或方法中使用的泛型参数都被替换了。
因为在定义泛型类时,不知道用户指定的类型值是什么,所以类中使用类型变量的属性和方法都不能确定,那么也就不能去调用类型不确定对象的方法(但可以调用Object类具有的方法)。
5 继承泛型类时的两种赋值方式
给出类型常量
给出类型变量
6 定义泛型接口
9 实现泛型接口两种赋值方式
给出类型常量
给出类型变量
泛型方法
1 泛型方法
java中不只可以定义泛型类,还可以定义泛型方法。
泛型方法被调用时需要指定泛型参数的值,这一点与创建泛型类的对象一样。
泛型方法可以定义在泛型类中,也可以定义在非泛型类中。泛型方法可以是static的,也可以不是static的。这与泛型和static没有半毛钱关系!
2 定义泛型方法
定义泛型方法的格式为:
修饰符 <类型参数> 返回值 方法名(参数列表)
例如:
public class MyClass {
public <T> void fun(T t) {
System.out.println(t.getClass().getName());
}
}
通常泛型方法的参数都会使用类型变量。上例中的参数就使用类型变量T!在使用者调用这个方法时会给T赋值,那么参数t的类型也就确定了。
3 调用泛型方法
通常泛型方法的参数都会使用类型变量,所以一般不需要显示为泛型方法指定类型参数的值。而是通过调用时传递的实际参数的类型隐示给类型参数赋值的。例如:
MyClass mc = new MyClass();
mc.fun(“hello”);
因为调用fun()方法时实参的类型为String,那么这就隐示为fun()方法的类型参数赋值为String了。
当然也可以显示指定泛型方法的类型参数,这需要在点前面指定类型值。例如:
mc.<String>(“hello”);
泛型边界
1 泛型边界限定的类型值的范围
通常我们看到的泛型类都没有边界限定,也就是说可以给泛型类的类型变量赋任意类型的值(当然基本类型是不可以的)。
java允许给类型参数指定边界,这样在指定类型参数的值时就必须在边界之内。
2 带有边界的泛型类
带有边界的泛型类需要使用extends关键字为类型变量指定边界,格式如下:
class 类名<变量名 extends 边界类型> {…}
例如:
class MyClass<T extends Person> {…}
3 创建带有边界的泛型类对象
MyClass mc = new MyClass();//在没有指定泛型参数的值时,类型变量的值就是边界类型了。也就是Person类型了。
因为MyClass类指定了边界Person,那么只能为类型变量赋值为Person或Person的子类了。
MyClass<Student> mc =new MyClass<Student>();
4 多个边界类型
还可以为类型参数指定多个边界类型,格式为:
class 类名<类型变量名 extends 边界类型1 & 边界类型2…> {…}
当为类型变量指定多个边界类型时,那么最多只能有一个是类类型,其他的都必须是接口类型。这个道理就不用说了吧。
通配符
1 错误理解泛型
现在有Person和Student类,其中Student是Person的子类。这说明:
Person p = new Student();
是正确的。
下面代码编译也是通过的,但运行时会抛出ArrayStoreException。
Person[] ps = new Student[10]; ps[0] = new Student(); ps[1] = new Employee(); |
在泛型中java就不在允许这么方式了:
ArrayList<Person> list = new ArrayList<Student>(); |
上面代码是编译不通过的,也就是说Person虽然是Student的父类,但ArrayList<Person>不是ArrayList<Student>的父类。
这也就是说,不能使用ArrayList<Student>类型的对象来调用参数为ArrayList<Person>类型的方法了。这真是太可惜了!
如果现在有方法用来打印封装Person的集合对象,即参数类型为List<Person>。但我们不能让这个方法打印List<Student>,这不是很可惜么?
public static void fun(List<Person> list) { for(Person p : list) { System.out.println(p); } } |
2 子类型通配符
l List<? extends Person>可以把new ArrayList<Person>,以及new ArrayList<Student>赋值它。即Person或Person子类型的集合。
l 一定转型为List<? extends Person>,那么只能使用返回值为E的方法,不能使用参数为E的方法。当然,不使用E的方法还是可以正常使用的!
l 通配符不能在new中使用,只能在引用中使用。
l List<? extends Person> aList = new ArrayList<? exstends Person>();
l 左边可以使用通配符,但右边new时不能使用通配符!
为了处理上面的问题,java提供了通配符。尝试把上面的fun()方法参数类型修改为List<? extends Person>类型:
public static void fun(List<? extends Person> list) { for(Person p : list) { System.out.println(p); } } |
我们可以把List<Student>、List<Employee>、List<Person>类型的对象赋值给List<? extends Person>类型的引用。
?并不是变量,而是确定的值。它表示一种确定下来的值,但我们不知道它是什么类型罢了。也就是说List<? extends Person>的元素类型已经开始不明确了,我们只知道元素类型是Person或Person的某种子类型,但具体是哪一个类型。这也说明我们不能向它添加东西了。
List<? extends Person> list1 = new ArrayList<Student>();//ok List<? extends Person> list2 = new ArrayList<Employee>();//ok list1.add(new Student());//error list1.add(new Employee());//error list2.add(new Student());//error list2.add(new Employee());//error |
上面代码中,对list1、list2添加什么元素都是错误的。让我们分析一下,对编译器而言,它不知道list1、list2指向的是实体是什么类型。如果允许了list1.add(new Student()),那么说明也要允许list1.add(new Employee())。因为对list1这个引用而言,它是List<? extends Person>类型,元素类型表示一种不知道的但却是确定的类型,但只知道它是Person的子类型。如果可以添加Student,那为什么不能添加Employee呢?所以编译器的态度是添加什么东西都不行!
也就是说,当给类型参数E赋值为<? extends 类型>时,那么对于参数为E的方法就不能再使用了,因为传递什么类型的参数都是错误的。
3 父类型通配符
还可以给类型参数赋值为<? super 类型>,这就是父类型通配符。
如果你需要一个方法,给参数List添加元素。
public static void fun(List<Person> list, Person p) { list.add(p); } |
你不能使用List<Student>来调用上面的方法,这个道理我们应该已经明白了。但你可能会说可以把方法参数类型修改为List<? extends Person>类型,这样就可以把List<Person>类型的值传递给参数了。但是如果参数类型为List<? extends Person>类型,那么就不能调用add()方法了!!!
这时可以把参数类型指定为List<? super Student>类型,你可以把List<Student>、List<Preson>,甚至List<Object>类型的对象赋值给它。
List<? extends Person>表示元素类型为不知道的确定类型,只知道元素类型为Student的父类型,但不知道是哪个父类型!也许是Person,也许是Object类型。无论是哪一种父类型,说明我们可以向集合对象添加Student对象!因为把Student类型对象添加到List<Person>或List<Object>中都是可以的!
但要注意,这时你就不能再使用get()方法了,因为元素的类型不知道是什么,只知道是Student类型的父类型,哪一种就不知道了,这说明你不能用Peson引用指向get()方法的返回值。
但可以使用Object引用来指向get()方法的返回值,因为Object是所有类的父类!
4 无界通配符
无界通配符:List<?>!
你可以使用List<?>的引用指向List<? extends Person>、List<? super Stuent>、List<Person>、List<Student>等等,即没有指定泛型的List可以指向什么,它就可以引向什么。
List<?> list1 = new ArrayList(); List<?> list2 = new ArrayList<Object>(); List<? extends Person> pList = new ArrayList<Person>(); List<?> list3 = pList; List<? super Student> sList = new ArrayList<Student>(); List<?> list4 = sList; |
呵呵~,你不能使用List<?>引用调用add()方法,也只能使用Object引用来指向get()方法的返回值了。
其实大多数情况下List<?>就是一种装饰!基本与List一样,没有什么区别了!但如果你使用List会有警告,而使用List<?>时就没有警告了!因为傻瓜编译器看到了你使用List<?>说明你在使用泛型的集合类,它会很开心!
5 通配符总结
可以使用List<? extends Person>指向List<Person>、List<Student>、List<Employee>。只要元素类型为Person,或其子类就是可以的。
可以使用List<? super Student>指向List<Student>、List<Person>、List<Object>。只要元素类型为Student或其Student父类就是可以的。
小心,你不能在new时使用通配符:
new ArrayList<? extends Person>(),这是不可以的!
但可以在定义引用时使用通配符:
ArrayList<? extends Person> list;
最后
以上就是迅速巨人为你收集整理的黑马程序员---泛型泛型的全部内容,希望文章能够帮你解决黑马程序员---泛型泛型所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复