常用符号 | |
---|---|
? | 表示不确定的java类型 |
T - Type | 表示具体的一个java类型 |
K - Key(键) V - Value(值) | 分别代表java键值中的Key Value |
E - Element | 在集合中使用,因为集合中存放的是元素 |
N - Number | 数值类型 |
1.泛型
泛型 | |||
---|---|---|---|
概念 | 即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢? 就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参) | ||
使用方式 | 泛型类 | ||
泛型方法 | |||
泛型接口 | |||
泛型通配 | 非限定通配 | ? | 等价于: ?extends Object,可用任何类型替代 |
限定通配 | ? extends T | T 或 T 的一个子类型 | |
? super T | T 或 T 的一个父类型 |
1.1泛型类
问题:为什么要使用泛型类?
(1)不使用泛型类
public class TestClass {
Object obj;
public TestClass(Object obj) {
this.obj = obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
public Object getObj() {
return obj;
}
public void showObj() {
System.out.println("T的实际类型为:" + obj.getClass());
}
public static void main(String[] args) {
//Java中,Object类是所有类的父类。可以将一个普通类对象作为参数传递给Object类对象。这在继承中叫做“上转型对象”(将子类对象的引用赋值给父类对象)
TestClass intObj = new TestClass(new Integer(33));
intObj.showObj();
int i = (Integer)intObj.getObj();
System.out.println(i);
TestClass StringObj = new TestClass(new String("This is a string"));
StringObj.showObj();
String s = (String)StringObj.getObj();
System.out.println(s);
}
}
int i = (Integer)intObj.getObj();
Q:类的对象和基本数据类型可以互相赋值吗?
A:可以。这种操作在JDK 5之后的版本允许使用
概念 | 举例(以 Integer 类为例) | 转换过程 |
---|---|---|
自动拆箱(autounboxing) | Integer obj = new Integer(10); int i = obj | i = obj.intValue() |
自动装箱(autoboxing) | Integer obj = 10 | 10---> Integer.valueof(10); obj = Integer.valueof(10) |
private final int value;
public Integer(int value) {
this.value = value;
}
public Integer(String string) throws NumberFormatException {
this(parseInt(string));
}
public static Integer valueOf(int i) {
return i >= 128 || i < -128 ? new Integer(i) : SMALL_VALUES[i + 128];
}//如果i小于-128或者大于等于128,就创建一个Integer对象,否则执行SMALL_VALUES[i + 128]```
(2)使用泛型类
public class GenericClass<T> {
T obj;
public GenericClass(T obj) {
this.obj = obj;
}
public void setObj(T obj) {
this.obj = obj;
}
public T getObj() {
return obj;
}
public void showObj() {
System.out.println("T的实际类型为:" + obj.getClass());
}
public static void main(String[] args) {
GenericClass<Integer> intObj = new GenericClass<Integer>(33);
intObj.showObj();
int i = intObj.getObj();
System.out.println(i);
GenericClass<String> StringObj = new GenericClass<String>("This is a string");
StringObj.showObj();
String s = StringObj.getObj();
System.out.println(s);
}
}
可以看出,不使用泛型类需要使用强制类型转换,比泛型类麻烦
以上两段代码的输出结果相同。
1.2泛型方法
public class GenericMethodDemo {
public static void main(String[] args ) {
Integer[] integers = {1, 2, 3, 4, 5};
String[] strings = {"London", "Paris", "New York", "Austin"};
GenericMethodDemo.<Integer>print(integers);
GenericMethodDemo.<String>print(strings);
}
public static <E> void print(E[] list) {
for (int i = 0; i < list.length; i++)
System.out.print(list[i] + " ");
System.out.println();
}
}
2. 通配泛型
三个符号 | |||
---|---|---|---|
非限定通配 | ? | 不对传入的泛型实参进行边界限制(实际上限制参数上界为Object) | 等价于: ?extends Object,可用任何类型替代 |
限定通配 | ? extends T | 为传入的泛型类型实参进行上下边界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类 | T 或 T 的一个子类型 |
? super T | T 或 T 的一个父类型 |
(1)?extends T
//输出栈中最大元素
public class WildCardNeedDemo {
public static void main(String[] args ) {
GenericStack<Integer> intStack = new GenericStack<>();
intStack.push(1); // 1 is autoboxed into new Integer(1)
intStack.push(2);
intStack.push(-2);
// Error: System.out.print("The max number is " + max(intStack));
}
/** Find the maximum in a stack of numbers */
public static double max(GenericStack<Number> stack) {
double max = stack.pop().doubleValue(); // initialize max
while (!stack.isEmpty()) {
double value = stack.pop().doubleValue();
if (value > max)
max = value;
}
return max;
}
}
上例9行将出现编译错误,虽然Integer是Number的 子类,但GenericStack<Integer> 并不是GenericStack<Number>的子类型。
正解:public static double max(GenericStack stack)改为
public static double max(GenericStack<? extends Number> stack)
(2)?super T
//将字符串栈 stack1 中的 字符串 添加到对象栈 stack2
public class SuperWildCardDemo {
public static void main(String[] args) {
GenericStack<String> stack1 = new GenericStack<String>();
GenericStack<Object> stack2 = new GenericStack<Object>();
stack2.push("Java");
stack2.push(2);
stack1.push("Sun");
add(stack1, stack2);
AnyWildCardDemo.print(stack2);
}
public static <T> void add(GenericStack<T> stack1,
GenericStack<? super T> stack2) {
while (!stack1.isEmpty())
stack2.push(stack1.pop());
}
}
此例public static void add(GenericStack<T> stack1, GenericStack<? super T> stack2)
用法1。
改为public static void add(GenericStack<? extends T> stack1, GenericStack<T> stack2)
用法2。也是正确的。
???怎么确定是用 ? extends T 还是 ? super T ???
上表中所说的受限通配和下限通配,这个界限其实是以 T 为界限的,以 例2.(2)代码为例 | |
---|---|
用法1 | 用法2 |
传入stack1、stack2,此时T代表stack1,?super T代表stack2 | 传入stack1、stack2,此时? extends T代表stack1,T代表stack2 |
以T(stack1)为界 | 以T(stack2)为界 |
?super T表示T 或 T的父类, stack1是String类对象, stack2是Object类对象, Object是String的父类 | ? extends T表示T 或 T的子类, stack1是String类对象, stack2是Object类对象, String是Object的子类 |
3. T,Object,?的区别
< T > 等同于 < T extends Object>
< ? > 等同于 < ? extends Object>
Object和T区别 | ?和T区别 |
---|---|
Object是一个实打实的类,并没有泛指谁,而T可以泛指Object,比方public void printList(List<T> list){}方法中可以传入List<Object> list类型参数,也可以传入List<String> list类型参数,但是public void printList(List<Object> list){}就只可以传入List<Object> list类型参数,因为Object类型并没有泛指谁,是一个确定的类型 | ?是一个不确定类,?和T都表示不确定的类型 ,但如果是T的话,函数里面可以对T进行操作,比方 T car = getCar(),而不能用? car = getCar() |