玖叶教程网

前端编程开发入门

【每日一学】面试必备:Java泛型问题全解析,高效应对挑战

学习总目标

本次学习目标

3 泛型方法

3.1 泛型方法的调用

在java.util.Arrays数组工具类中,有很多泛型方法,例如:

?public static List asList(T… a):将实参对象依次添加到一个固定大小的List列表集合中。

?public static T[] copyOf(T[] original, int newLength):复制任意对象数组,新数组长度为newLength。

?…

package com.atguigu.genericmethod;

import java.util.Arrays;
import java.util.List;

public class TestArrays {
public static void main(String[] args) {
List<String> list = Arrays.asList("java", "world", "hello", "atguigu");
System.out.println(list);

String[] arr = {"java", "world", "hello"};
String[] strings = Arrays.copyOf(arr, arr.length * 2);
System.out.println(Arrays.toString(strings));
}
}

泛型方法在调用时,由实参的类型确定泛型方法类型变量的具体类型。

3.2 自定义泛型方法

前面介绍了在定义类、接口时可以声明,在该类的方法和属性定义、接口的方法定义中,这些可被当成普通类型来用。但是,在另外一些情况下,

(1)如果我们定义类、接口时没有使用,但是某个方法形参类型不确定时,这个方法可以单独定义;

(2)另外我们之前说类和接口上的类型形参是不能用于静态方法中,那么当某个静态方法的形参类型不确定时,静态方法可以单独定义。

语法格式:

【修饰符】 <类型变量列表> 返回值类型 方法名(【形参列表】)【throws 异常列表】{
//...
}

?:可以是一个或多个类型变量,一般都是使用单个的大写字母表示。例如:、等。

示例代码:

我们编写一个数组工具类,包含可以给任意对象数组进行从小到大排序,调用元素对象的compareTo方法比较元素的大小关系。

package com.atguigu.genericmethod;

public class MyArrays {
public static <T> void sort(T[] arr){
for (int i = 1; i < arr.length; i++) {
for (int j = 0; j < arr.length-i; j++) {
if(((Comparable<T>)arr[j]).compareTo(arr[j+1])>0){
T temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
}

package com.atguigu.genericmethod;

import com.atguigu.generic.Circle;

import java.util.Arrays;

public class MyArraysTest {
public static void main(String[] args) {
int[] arr = {3,2,5,1,4};
// MyArrays.sort(arr);//错误的,因为int[]不是对象数组

String[] strings = {"hello","java","chai"};
MyArrays.sort(strings);
System.out.println(Arrays.toString(strings));

Circle[] circles = {new Circle(2.0),new Circle(1.2),new Circle(3.0)};
MyArrays.sort(circles); //编译通过,运行报错,Circle没有实现Comparable接口
}
}

4 类型变量的上限与泛型的擦除

4.1 类型变量的上限

当在声明类型变量时,如果不希望这个类型变量代表任意引用数据类型,而是某个系列的引用数据类型,那么可以设定类型变量的上限。

语法格式:

<类型变量 extends 上限>

如果有多个上限

<类型变量 extends 上限1 & 上限2>

如果多个上限中有类有接口,那么只能有一个类,而且必须写在最左边。接口的话,可以多个。

如果在声明时没有指定任何上限,默认上限是java.lang.Object。

1、定义泛型类的类型变量时指定上限

例如:我们要声明一个两个数算术运算的工具类,要求两个数必须是Number数字类型,并且实现Comparable接口。

package com.atguigu.limmit;

import java.math.BigDecimal;
import java.math.BigInteger;

public class NumberTools<T extends Number & Comparable<T>>{
private T a;
private T b;

public NumberTools(T a, T b) {
super();
this.a = a;
this.b = b;
}

public T getSum(){
if(a instanceof BigInteger){
return (T) ((BigInteger) a).add((BigInteger)b);
}else if(a instanceof BigDecimal){
return (T) ((BigDecimal) a).add((BigDecimal)b);
}else if(a instanceof Byte){
return (T)(Byte.valueOf((byte)((Byte)a+(Byte)b)));
}else if(a instanceof Short){
return (T)(Short.valueOf((short)((Short)a+(Short)b)));
}else if(a instanceof Integer){
return (T)(Integer.valueOf((Integer)a+(Integer)b));
}else if(a instanceof Long){
return (T)(Long.valueOf((Long)a+(Long)b));
}else if(a instanceof Float){
return (T)(Float.valueOf((Float)a+(Float)b));
}else if(a instanceof Double){
return (T)(Double.valueOf((Double)a+(Double)b));
}
throw new UnsupportedOperationException("不支持该操作");
}

public T getSubtract(){
if(a instanceof BigInteger){
return (T) ((BigInteger) a).subtract((BigInteger)b);
}else if(a instanceof BigDecimal){
return (T) ((BigDecimal) a).subtract((BigDecimal)b);
}else if(a instanceof Byte){
return (T)(Byte.valueOf((byte)((Byte)a-(Byte)b)));
}else if(a instanceof Short){
return (T)(Short.valueOf((short)((Short)a-(Short)b)));
}else if(a instanceof Integer){
return (T)(Integer.valueOf((Integer)a-(Integer)b));
}else if(a instanceof Long){
return (T)(Long.valueOf((Long)a-(Long)b));
}else if(a instanceof Float){
return (T)(Float.valueOf((Float)a-(Float)b));
}else if(a instanceof Double){
return (T)(Double.valueOf((Double)a-(Double)b));
}
throw new UnsupportedOperationException("不支持该操作");
}
}

测试类

package com.atguigu.limmit;

public class NumberToolsTest {
public static void main(String[] args) {
NumberTools<Integer> tools = new NumberTools<Integer>(8,5);
Integer sum = tools.getSum();
System.out.println("sum = " + sum);
Integer subtract = tools.getSubtract();
System.out.println("subtract = " + subtract);
}
}

2、定义泛型方法的类型变量时指定上限

我们编写一个数组工具类,包含可以给任意对象数组进行从小到大排序,调用元素对象的compareTo方法比较元素的大小关系。要求数组的元素类型必须是java.lang.Comparable接口类型。

package com.atguigu.limmit;

public class MyArrays {
public static <T extends Comparable<T>> void sort(T[] arr){
for (int i = 1; i < arr.length; i++) {
for (int j = 0; j < arr.length-i; j++) {
if(arr[j].compareTo(arr[j+1])>0){
T temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
}

测试类

package com.atguigu.limmit;

import com.atguigu.generic.Circle;

import java.util.Arrays;

public class MyArraysTest {
public static void main(String[] args) {
int[] arr = {3,2,5,1,4};
// MyArrays.sort(arr);//错误的,因为int[]不是对象数组

String[] strings = {"hello","java","chai"};
MyArrays.sort(strings);
System.out.println(Arrays.toString(strings));

Circle[] circles = {new Circle(2.0),new Circle(1.2),new Circle(3.0)};
// MyArrays.sort(circles); //编译报错
}
}

4.2 泛型擦除

当使用参数化类型的类或接口时,如果没有指定泛型,那么会怎么样呢?

会发生泛型擦除,自动按照最左边的第一个上限处理。如果没有指定上限,上限即为Object。

package com.atguigu.limmit;

import java.util.ArrayList;
import java.util.Collection;

public class TestErase {
public static void main(String[] args) {
NumberTools tools = new NumberTools(8,5);
Number sum = tools.getSum();//自动按照Number处理
System.out.println("sum = " + sum);
Number subtract = tools.getSubtract();
System.out.println("subtract = " + subtract);

Collection coll = new ArrayList();
coll.add("hello");
coll.add(1);
for (Object o : coll) {//自动按照Object处理
System.out.println(o);
}
}
}

5 类型通配符

5.1 Java泛型指定限制问题

声明一个方法,形参是Collection,但是元素类型不确定,怎么办?

package com.atguigu.wildcard;

import java.util.ArrayList;
import java.util.Collection;

public class TestProblem {
public static void m1(Collection<Object> coll){
for (Object o : coll) {
System.out.println(o);
}
}
public static void m2(Collection coll){
for (Object o : coll) {
System.out.println(o);
}
}
public static <T> void m3(Collection<T> coll){
for (T o : coll) {
System.out.println(o);
}
}

public static void main(String[] args) {
m1(new ArrayList<Object>());//Collection coll = new ArrayList ();
m1(new ArrayList<>());//Collection coll = new ArrayList<>();//与上面完全等价
m1(new ArrayList());//Collection coll = new ArrayList();//有警告
// m1(new ArrayList());//Collection coll = new ArrayList();//错误

//编译看左边,左边泛型擦除,此处泛型按照Object处理,右边泛型指定啥都没用
m2(new ArrayList<Object>());//Collection coll = new ArrayList ();
m2(new ArrayList<>());//Collection coll = new ArrayList<>();//与上面完全等价
m2(new ArrayList());//Collection coll = new ArrayList();//泛型擦除
m2(new ArrayList<String>());//Collection coll = new ArrayList();

m3(new ArrayList<Object>());//Collection coll = new ArrayList ();
m3(new ArrayList<>());//Collection<> coll = new ArrayList<>();//与上面完全等价
m3(new ArrayList());//Collection coll = new ArrayList();//有警告
m3(new ArrayList<String>());//Collectioncoll = new ArrayList();
}
}


5.2 类型通配符

当我们声明一个变量/形参时,这个变量/形参的类型是一个泛型类或泛型接口,例如:Comparator类型,但是我们仍然无法确定这个泛型类或泛型接口的类型变量的具体类型,此时我们考虑使用类型通配符 ? 。

package com.atguigu.wildcard;

import java.util.ArrayList;
import java.util.Collection;

public class TestWildcard {
public static void m4(Collection coll){
for (Object o : coll) {
System.out.println(o);
}
}

public static void main(String[] args) {
//右边泛型指定为任意类型或不指定都可以
m4(new ArrayList<Object>());//Collection coll = new ArrayList ();
m4(new ArrayList<>());//Collection coll = new ArrayList<>();
m4(new ArrayList());//Collection coll = new ArrayList();
m4(new ArrayList<String>());//Collection coll = new ArrayList();
}
}


5.3 类型通配符的三种使用形式

类型通配符 ? 有三种使用形式:

?:完整形式为:类名 或接口名,此时?代表任意类型。

?:完整形式为:类名 或接口名,此时?代表上限类型本身或者上限的子类,即?代表 <= 上限的类型。

?:完整形式为:类名 或接口名,此时?代表下限类型本身或者下限的父类,即?代表>= 下限的类型。

案例:

声明一个集合工具类MyCollections,要求包含:

?public static boolean different(Collection c1, Collection c2):比较两个Collection集合,此时两个Collection集合的泛型可以是任意类型,如果两个集合中没有相同的元素,则返回true,否则返回false。

?public static void addAll(Collection c1, T… args):可以将任意类型的多个对象添加到一个Collection集合中,此时要求Collection集合的泛型指定必须>=元素类型。

?public static void copy(Collection dest,Collection src):可以将一个Collection集合的元素复制到另一个Collection集合中,此时要求原Collection泛型的类型<=目标Collection的泛型类型。

package com.atguigu.wildcard;

import java.util.Collection;

public class MyCollections {
public static boolean different(Collection c1, Collection c2){
for (Object o : c1) {
if(c2.contains(o)){
return false;
}
}
return true;
}

public static <T> void addAll(Collection super T> c1, T... args){
for (int i = 0; i < args.length; i++) {
c1.add(args[i]);
}
}

public static <T> void copy(Collection super T> dest,Collection extends T> src){
for (T t : src) {
dest.add(t);
}
}

}

测试类

package com.atguigu.wildcard;

import java.util.ArrayList;
import java.util.Collection;

public class MyCollectionsTest {
public static void main(String[] args) {
Collection<Integer> c1 = new ArrayList<Integer>();
MyCollections.addAll(c1,1,2,3,4,5);
System.out.println("c1 = " + c1);

Collection<String> c2 = new ArrayList<String>();
MyCollections.addAll(c2,"hello","java","world");
System.out.println("c2 = " + c2);

System.out.println("c1 != c2 " + MyCollections.different(c1, c2));

Collection<Object> c3 = new ArrayList<>();
MyCollections.copy(c3,c1);
MyCollections.copy(c3,c2);
System.out.println("c3 = " + c3);
}
}

5.4 使用类型通配符来指定类型参数的问题

package com.atguigu.wildcard;

import java.util.ArrayList;
import java.util.Collection;

public class TestWildcardProblem {
public static void main(String[] args) {
Collection c1 = new ArrayList<>();
// c1.add(new Object());
// c1.add("hello");
// c1.add(1);
//不能添加,c1只读


Collection extends Object> c2 = new ArrayList<>();
// c2.add(new Object());
// c2.add("hello");
// c2.add(1);
//不能添加,c2只读

Collection super Number> c3 = new ArrayList<>();
// c3.add(new Object());
// c3.add("hello");
c3.add(1);
//可以添加Number对象或Number子类对象
}
}

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言