Java基础第一篇 - 泛型
泛型
1. 入门:
泛型的引入是为了让Java能够记住集合中的数据类型,而不是统一用Object来处理。
例如:
1 | public class ListErr |
这个时候程序会报ClassCastException异常。
我们可以通过手动实现编译时检查类型。通过组合的方式来复用ArrayList类。
1 | class StrList { |
而当我们使用泛型的时候如下:
1 | public class GenericList { |
通过一个<>指定参数类型,就不会发生上面那种情况了。
2. 深入泛型
泛型能够在哪些场景下使用呢?
定义类,接口,方法时使用类型形参,这个类型形参将在声明变量,创建对象,调用方法时动态指定。
1 | public interface List<E> |
除了在集合类里面使用泛型,我们也可以自定义泛型类,例如
1 | public class Apple<T> |
上面定义了一个带泛型声明的Apple
注意: 定义构造器不需要加<>。
3. 泛型类派生子类
1 | public class A extends Apple<T>{} // 错误的,不能再包含类型形参 |
正确写法:
1 | public class A extends Apple<String> |
这个时候,getInfo() 和 void setInfo(String info)都会由T被替换成为String
当然,也可以不写类型参数
1 | public class A extends Apple |
这样写也是正确的,但是Java编译器可能会发出警告:使用了未经检查或者不安全的操作-这就是泛型检查的警告。
4. 并不存在泛型类
其实ArrayList
但是系统并没有生成新的class文件。
1 | List<String> 11 = new ArrayList(); |
结果输出true.说明无论是实际类型参数是什么,他们运行时都是同样的类。
因为泛型在Java中都被当成同一类处理,内存中也只占用一块内存空间,因此静态方法,静态变量等声明不允许使用类型参数。
5. 使用类型通配符
? 是元素类型未知的意思,也是Java里的类型通配符。
1 | public class test(List<?> c){ |
设定类型通配符的上限
例如:定义一个类shape,有两个子类circle rectangle
再定义一个Canvas类
1 | public class Canvas |
这个时候,如果调用
1 | List<Circle> circleList = new ArrayList<>(); |
就会报错,因为List
但是这个方法有个不好的地方,就是需要进行强制类型转换,过于繁琐。因此我们可以使用被限制的泛型通配符
List<? extends shape>
这就代表了所有shape泛型List的父类
那么像List
设定类型形参的上限
1 | public class Apple<T extends Number> |
表明了col的上限是Number类,传入参数时只能是Number或者Number类的子类。
6. 泛型方法
用法格式如下:
1 | 修饰符<T, S> 返回值类型 方法名(形参列表) |
例如:
1 | public <T> void fromArrayToCollection (T[] a, Collection<T> c) |
声明一个泛型方法,带有T类型形参。这个T可以在该方法内当成普通类型使用。并且这个T可以通过系统自动推断类型,不需要显示传入。
7. 泛型方法和类型通配符的区别
通常情况下泛型方法都可以替代类型通配符。例如:
1 | // 类型通配符 |
可以采用泛型方法
1 | public interface Collection<E> |