java:泛型 & 通配泛型之区别、详解

常用符号
? 表示不确定的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()

猜你喜欢

转载自blog.csdn.net/weixin_42250302/article/details/89065830