Java泛型
什么是泛型
泛型就是定义一种模板,例如ArrayList,然后在代码中为用到的类创建对应的ArrayList<类型>
泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?
顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),
然后在使用/调用时传入具体的类型(类型实参)。
泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,
操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
假定我们有这样一个需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?
答案是可以使用 Java 泛型。
使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序。
泛型方法
你可以写一个泛型方法,该方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。
下面是定义泛型方法的规则:
- 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的)。
- 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
- 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
- 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。
public class JavaDemo {
public static <E> void printArray(E[] intputArray) {
for (E element : intputArray) {
System.out.printf("%s ", element);
// System.out.println(element);
}
System.out.println();
}
public static void main(String[] args) {
Integer[] intArray = {1, 2, 3, 4, 5, 6, 2};
Double[] doubleArray = {1.1, 2.2, 3.3, 4.4, 5.5};
Character[] charArray = {'h', 'e', 'l', 'l', 'o'};
printArray(intArray);
printArray(doubleArray);
printArray(charArray);
}
}
1 2 3 4 5 6 2
1.1 2.2 3.3 4.4 5.5
h e l l o
设置有界类型参数
有界的类型参数:
可能有时候,你会想限制那些被允许传递到一个类型参数的类型种类范围。例如,一个操作数字的方法可能只希望接受Number或者Number子类的实例。这就是有界类型参数的目的。
要声明一个有界的类型参数,首先列出类型参数的名称,后跟extends关键字,最后紧跟它的上界。
实例
下面的例子演示了"extends"如何使用在一般意义上的意思"extends"(类)或者"implements"(接口)。该例子中的泛型方法返回三个可比较对象的最大值。
class maxNum {
// public static <T extends Comparable<T>> T maximum(T x, T y, T z)
//比较三个值并返回最大值
//继承Compareble类
public static <T extends Comparable<T>> T maxnum(T x, T y, T z) {
T max = x; //假设x是初始最大值
if (y.compareTo(max) > 0) {
max = y;
}
if (z.compareTo(max) > 0) {
max = z;
}
return max;
}
}
public class JavaDemo {
public static void main(String[] args) {
maxNum max = new maxNum();
System.out.printf("%d,%d,%d中最大的数为%d\n", 3, 4, 5, max.maxnum(3, 4, 5));
System.out.printf("%.1f,%.1f,%.1f中最大的数为%.1f\n", 1.1, 2.2, 3.3, max.maxnum(1.1, 2.2, 3.3));
System.out.printf("%s,%s,%s中最大的数为%s\n", "apple", "orange", "pear", max.maxnum("apple", "orange", "pear"));
}
}
3,4,5中最大的数为5
1.1,2.2,3.3中最大的数为3.3
apple,orange,pear中最大的数为pear
泛型类
泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。
和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。
public class JavaDemo<T> {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return this.t;
}
public static void main(String[] args) {
JavaDemo<Integer> integerJavaDemo = new JavaDemo<Integer>();
JavaDemo<String> stringJavaDemo = new JavaDemo<String>();
integerJavaDemo.add(new Integer(10));
stringJavaDemo.add(new String("菜鸟教程"));
System.out.printf("整型值为:%d\n\n", integerJavaDemo.get());
System.out.printf("字符串为:%s\n", stringJavaDemo.get());
}
}
整型值为:10
字符串为:菜鸟教程
类型通配符
1、类型通配符一般是使用?代替具体的类型参数。例如 List<?> 在逻辑上是List,List 等所有List<具体类型实参>的父类。
import java.util.ArrayList;
import java.util.List;
public class JavaDemo {
public static void main(String[] args) {
List<String> name = new ArrayList<String>();
List<Integer> age = new ArrayList<Integer>();
List<Number> number = new ArrayList<Number>();
name.add("icon");
age.add(18);
number.add(314);
getData(name);
getData(age);
getData(number);
}
public static void getData(List<?> data) {
System.out.println("data:" + data.get(0));
}
}
解析: 因为getData()方法的参数是List类型的,所以name,age,number都可以作为这个方法的实参,这就是通配符的作用
设置类型通配符上限
2、类型通配符上限通过形如List来定义,如此定义就是通配符泛型值接受Number及其下层子类类型。
类似于泛型方法设置有界类型参数
实例
import java.util.ArrayList;
import java.util.List;
public class JavaDemo {
public static void main(String[] args) {
List<String> name = new ArrayList<String>();
List<Integer> age = new ArrayList<Integer>();
List<Number> number = new ArrayList<Number>();
name.add("icon");
age.add(18);
number.add(314);
// getData(name);
// getData(age);
// getData(number);
// getUperNumber(name); //出现一个 不兼容的类型 错误
getUperNumber(age);
getUperNumber(number);
}
public static void getData(List<?> data) {
System.out.println("data:" + data.get(0));
}
public static void getUperNumber(List<? extends Number> data) {
System.out.println("data:"+data.get(0));
}
}
data:18
data:314
解析: 在(//1)处会出现错误,因为getUperNumber()方法中的参数已经限定了参数泛型上限为Number,所以泛型为String是不在这个范围之内,所以会报错
3、类型通配符下限通过形如 List<? super Number>来定义,表示类型只能接受Number及其三层父类类型,如 Object 类型的实例。
集合中的泛型
- 集合默认接受Object的类型,可以放入任何对象
- 集合使用泛型,则只能放入指定类型的对象,或者指定类型的子类对象
泛型的简写
正常写法:ArrayList heros = new ArrayList();
简写:ArrayList heros2 = new ArrayList<>();
实例:
package fanxing;
import java.lang.reflect.Array;
import java.util.ArrayList;
/**
* 集合中的泛型
*
*
*
* @author Aike
* @create 2021-05-16 16:48
*/
class Hero{
public String name;
}
class ADHero extends Hero{
}
class APHero extends Hero{
}
public class test {
public static void main(String[] args) {
// ArrayList默认接受Object的对象
ArrayList list=new ArrayList();
list.add(new ADHero());
list.add(new APHero());
// 不使用泛型的ArrayList在对象转型上可能会报错
// APHero apHero=(APHero)list.get(0);
// 使用泛型的ArrayList
// 只能放入APHero类型的对象
ArrayList<APHero> list2=new ArrayList<>();
list2.add(new APHero());
// 假设泛型的类型为Hero,不仅Hero类型可以放进去,Hero类型的子类APHero和ADHero都可以放进去
ArrayList<Hero> list3=new ArrayList<>(); //泛型的简写
list3.add(new ADHero());
list3.add(new APHero());
// 根据数字类的知识,设计一个集合,这个集合里即可以放整数,也可以放浮点数,但是不能放字符串
ArrayList<Number> number=new ArrayList<>();
number.add(123);
number.add(123.00);
// number.add("213"); //无法进行插入,不能放入字符串
}
}
支持泛型的类
自定义泛型类
package fanxing;
import java.util.LinkedList;
/**
* 自定义泛型类
*
* @author Aike
* @create 2021-05-16 18:40
*/
class MyStack<T> {
LinkedList<T> values = new LinkedList<>();
// 插入push
public void push(T t) {
values.addLast(t);
}
// 查看最后一个peek
public T peek() {
return values.getLast();
}
// 移除最后一个pull
public void pull() {
values.removeLast();
}
}
public class test2 {
public static void main(String[] args) {
// 声明这个Stack,设置泛型<Hero>表示该Stack只能存放Hero对象
MyStack<Hero> heroStack=new MyStack<>();
heroStack.push(new Hero());
heroStack.push(new Hero());
}
}
通配符
? extends
泛型的通配符为:? extends表示可放入指定类型及其该类型的子类对象
ArrayList heroList<? extends Hero> 表示这是一个Hero泛型或者其子类泛型
从heroList取出来的对象,一定是可以转型成Hero的
//Hero为父类,APHero为子类
ArrayList<APHero> apHeroes=new ArrayList<>();
ArrayList<? extends Hero> extendList=apHeroes;
package fanxing;
import java.util.ArrayList;
/**
* 泛型通配符
* ? extends
* ? super
* ?
*
* @author Aike
* @create 2021-05-16 18:48
*/
public class test3 {
public static void main(String[] args) {
//? extends
//可以存放Hero及其子类
ArrayList<APHero> apHeroes=new ArrayList<>();
apHeroes.add(new APHero());
apHeroes.add(new APHero());
apHeroes.add(new APHero());
//将APHero类型的泛型集合转换成<? extends Hero>,<? extends Hero>泛型可以存放子类对象,所以使用子类类型的泛型进行初始化
ArrayList<? extends Hero> extendList=apHeroes;
//取出的对象一定可以转换成Hero类型
Hero hero=extendList.get(0);
}
}
? super
泛型的通配符为:? super表示可放入指定类型及其该类型的父类对象
ArrayList heroList<? super Hero> 表示这是一个Hero泛型或者其父类泛型
可以往里面插入Hero以及Hero的父类
ArrayList<? super Hero> superList=new ArrayList<Object>();
package fanxing;
import java.util.ArrayList;
/**
* 泛型通配符
* ? extends
* ? super
* ?
*
* @author Aike
* @create 2021-05-16 18:48
*/
public class test3 {
public static void main(String[] args) {
//? super
//可以存放Hero及其父类
//使用<Object>进行初始化
ArrayList<? super Hero> superList=new ArrayList<Object>();
superList.add(new Hero());
superList.add(new Hero());
superList.add(new Hero());
//取出的对象可能是Object,可能是Hero
// hero =superList.get(0); //会报错,因为取出的对象不一定可以隐式转换
Hero hero= (Hero) superList.get(0); //需要强制转换
}
}
?
任意类型的泛型,以Object形式取出
package fanxing;
import java.util.ArrayList;
/**
* 泛型通配符
* ? extends
* ? super
* ?
*
* @author Aike
* @create 2021-05-16 18:48
*/
public class test3 {
public static void main(String[] args) {
//?泛型
ArrayList<APHero> apHero=new ArrayList<>();
apHero.add(new APHero());
//任意类型的对象都可以放入?的泛型
ArrayList<?> generalList=apHero;
Object o=generalList.get(0);
}
}
通配符总结
- 如果希望只取出,不插入,就使用? extends Hero,因为取出的是一定可以强制转换成当前对象
- 如果希望只插入,不取出,就使用? super Hero,因为取出的可能是Object类型,不可强转
- 如果希望,又能插入,又能取出,就不要用通配符?,任意类型可插入,取出为Object形式
泛型转型
子类对象可以转换成父类对象
子类泛型不可以转换为父类泛型
package fanxing;
import java.lang.reflect.Array;
import java.util.ArrayList;
/**
* 泛型转型的检验
*
* @author Aike
* @create 2021-05-17 15:11
*/
public class test4 {
public static void main(String[] args) {
// 根据面向对象的知识,子类转父类是一定可以成功的
Hero hero = new Hero();
APHero apHero = new APHero();
// 子类转父类
hero = apHero;
// 子类泛型转父类泛型
ArrayList<APHero> apHeroes = new ArrayList<>();
ArrayList<Hero> heroes = new ArrayList<>();
heroes = apHeroes; //无法转换
}
}