之前我们完成了作业:通过Object可以实现某种重用(reuse),比如:让一个数组/栈可以装任何类型的对象(int,String,Student……)
很多时候,我们“潜意识”里面,期望的是:
Object[] students = { 32, 18, 19 }; Object[] students = { "lang", "atai", "bo" };不然的话,
Object[] students = { 32, "48", true };把数组里面元素取出来用的时候,你不知道取出来的对象究竟是什么类型,没法用!
System.out.println((int)students[1] * 2);
@想一想@:这种说法能不能成立?为什么?
数组里装着什么类型的数据,开发人员自己知道的呀……
class MimicStack<T> { public T pop() { //注意返回的是T,不是Object或者int return null; //模拟实现 } }语法点:
public MimicStack() { //... }
class MimicStack<T1,T2> {
“泛型”类还不是一个类,声明一个泛型类,实际上并没有确定一个类型
MimicStack<T> stack = null; //报错
只有在指定了泛型类型参数的时候,才确定了一个类型(我将其称之为具象化)
MimicStack<Integer> stack = null;
PS:因为Java的“伪”泛型特征,Java中的基本类型(比如:int),不能作为泛型参数,所以这里用Integer代替;C#中没有这种限制。
使用了不同类型参数的泛型类,不是同一个类:
MimicStack<Integer> stack = new MimicStack<Integer>(); //OK //error: can not convert //MimicStack<String> stack = new MimicStack<Integer>();
#常见面试题:为什么需要泛型?
类型安全(type safety):
MimicStack<Integer> stack = new MimicStack<Integer>();
stack.push("32"); //error: not applicable
Integer out = stack.pop(); //不再是 Object out = stack.pop();
泛型还可以用于接口/方法(以及我们后面要学的其他语法部分),但没有泛型enum。
interface MimicStack<T> { public void push(T element); public T pop(); }
static <T> T grow(T year) { //泛型参数的声明在返回前 return null; }
int result = grow(23);注意区别:泛型方法 vs 泛型类中使用了泛型参数的方法。
PS:C#中泛型方法的写法和Java略有不同
之前我们的泛型类型参数可以是任何类型,但有时候我们希望该参数类型有所约束,比如:必须是Student类及其子类。
class MimicStack<T extends Student> {这样的话:
MimicStack<Student> stack = new MimicStack<Student>(); //OK MimicStack<OnlineStudent> stack = new MimicStack<OnlineStudent>(); //OK MimicStack<Integer> stack = new MimicStack<Integer>(); //和Student无关,不行 MimicStack<Person> stack = new MimicStack<Person>(); //是Student的基类,不行
还可以约束参数必须实现了某个接口。
class Person implements IMove {}
class MimicStack<T extends IMove> {
MimicStack<Person> stack = new MimicStack<Person>(); //OK
注意:无论是基类还是接口,统一使用extends
可以为多个类型参数定义不同的约束
还可以要求类型参数既继承了某个基类,还实现了某个接口:
class MimicStack<T extends Student&IMove> { //使用&连接基类和接口
class OnlineStudent extends Student implements IMove{
MimicStack<OnlineStudent> stack = new MimicStack<OnlineStudent>();
仅仅是Type parameter不同,不算是不同的泛型类
class Person<T extends Major> { } //class Person<T extends SQL> { }
@想一想@:为什么需要这种约束呢?提示:
<T> T Add(T a, T b) { return a + b; //为什么会报错? }
PS:C#中泛型约束写法略有不同
一个已经具象化的泛型类可以认为就是一个非泛型类,非泛型类和泛型类之间可以互相继承:
class MimicStack<T> extends Person { class Person extends MimicStack<Integer>{
一个非泛型类不能继承一个还没有具象化的泛型类
class Person extends MimicStack<T>{ //编译错误:T不是类但是,泛型类可以继承一个非泛型类(又被称之为延迟具象)
class Student<T> extends MimicStack<T> {这时候,如果父类的泛型参数有约束:
class MimicStack<T extends Person> {
那么,
//父子泛型类可以有相同的约束 class Student<T extends Person> extends MimicStack<T> { //子类可以有比父类更“严格”的约束(Teacher是Person的子类) class Student<T extends Teacher> extends MimicStack<T> {
(泛型)父类变量一样可以装(泛型)子类对象
MimicStack<Student> stack = new Student()<Student>();注意:父子类两者都使用了相同的泛型参数。
@想一想@:使用父子类作为泛型参数,具象化同一个泛型类,得到的两个泛型对象,能有啥关系不?
class MimicStack<T extends Person> {
MimicStack<Person> stack = new MimicStack<Student>();
会报错,但实际上,还是有救的……详见:
分别使用C#/Java各自语法后完成:
多快好省!前端后端,线上线下,名师精讲
更多了解 加: