1.泛型
1.为什么需要泛型:
Java推出泛型以前,程序员可以构建一个元素类型为Object的集合,该集合能够存储任意的数据类型对象,而在使用该集合的过程中,需要程序员明确知道存储每个元素的数据类型,否则很容易引发ClassCastException异常。
2.泛型的定义:
Java泛型(generics)是JDK5中引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许我们在编译时检测到非法的类型数据结构。泛型的本质就是参数化类型,也就是所操作的数据类型被指定为一个参数。
3.泛型的优点:
- 编译期间检查类型,类型安全,确保不会发生ClassCation异常。
- 在获取集合里的元素的时候不需要强制类型转换,按集合里指定的类型接收
4.类型:
- E – Element (在集合中使用,因为集合中存放的是元素)
- T – Type(表示Java 类,包括基本的类和我们自定义的类)
- K – Key(表示键,比如Map中的key)
- V – Value(表示值)
- N – Number(表示数值类型)
- ? – (表示不确定的java类型)
- S、U、V – 2nd、3rd、4th types
import java.util.ArrayList;
public class Demo12 {
public static void main(String[] args) {
ArrayList<String> strList = new ArrayList<>();
strList.add("Java");
strList.add("Golang");
for (int i = 0; i < strList.size(); i++) {
String s = strList.get(i); //在获取集合里的元素的时候不需要类型转换,按集合里指定的类型接收
System.out.println(s);
}
System.out.println("----------------");
ArrayList<Integer> intList = new ArrayList<>();
intList.add(100);
intList.add(200);
for (int i = 0; i < intList.size(); i++) {
Integer integer = intList.get(i);
System.out.println(integer);
}
}
}
运行结果:
Java
Golang
----------------
100
200
2.泛型类
1.泛型类的定义方法:
class 类名称 <泛型标识,泛型标识,···>{
private 泛型标识 变量名:
······
}
2.常用泛型标识:T、E、K、V
3.使用语法:
类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>();
JDK1.7以后,后面的<>中的具体的数据类型可以省略不写,也称为菱形语法。
类名<具体的数据类型> 对象名 = new 类名<>();
4.注意事项:
- 泛型类在创建对象的时候,没有指定类型时将按照Object类型来操作
- 泛型的类型参数只能是类类型,不能是基本数据类型。
- 泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是相同的类型
/*<T> 泛型标识---类型实参
T 创建对象的时候往里制定具体的数据类型
*/
public class Demo01<T>{
private T key;
public Demo01(T key) {
this.key = key;
}
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
@Override
public String toString() {
return "Demo01{" +
"key=" + key +
'}';
}
}
public class Demo02 {
public static void main(String[] args) {
Demo01<String> stringDemo01 = new Demo01<>("java");
String key1 = stringDemo01.getKey();
System.out.println("key1:"+key1);
System.out.println("==========");
Demo01<Integer> integerDemo01 = new Demo01<>(100);
Integer key2 = integerDemo01.getKey();
System.out.println("key2:"+key2);
System.out.println("==========");
Demo01 objDemo01 = new Demo01("ABC");
Object key3 = objDemo01.getKey();
System.out.println("key3:"+key3);
System.out.println("==========");
System.out.println(stringDemo01.getClass());
System.out.println(integerDemo01.getClass());
System.out.println(stringDemo01.getClass()==integerDemo01.getClass());
}
}
运行结果:
key1:java
==========
key2:100
==========
key3:ABC
==========
class com.generic.Demo01
class com.generic.Demo01
true
1.案例
模拟抽奖器:
import java.util.ArrayList;
import java.util.Random;
/*
抽奖器
*/
public class ProductGetter<T> {
Random random = new Random();
//奖品
private T product;
//奖品池
ArrayList<T> list = new ArrayList<>();
//添加奖品
public void addProduct(T t){
list.add(t);
}
//抽奖
public T getProduct() {
T product = list.get(random.nextInt(list.size()));
return product;
}
}
public class MainClass {
public static void main(String[] args) {
//创建抽奖器对象,指定数据类型
ProductGetter<String> stringProductGetter = new ProductGetter<>();
//创建奖品
String[] strProducts = {"苹果手机","小米手机","空调","冰箱"};
//遍历,填充奖品
for (int i = 0; i < strProducts.length; i++) {
//调用泛型类的addProduct方法
stringProductGetter.addProduct(strProducts[i]);
}
//获取Product,抽奖
String product1 = stringProductGetter.getProduct(); //泛型类成员方法的调用
System.out.println("恭喜你,你抽中了:"+product1);
System.out.println("================");
ProductGetter<Integer> integerProductGetter = new ProductGetter<>();
int[] intProducts = {5000,6000,8000,20000};
for (int i = 0; i < intProducts.length; i++) {
integerProductGetter.addProduct(intProducts[i]);
}
Integer product2 = integerProductGetter.getProduct();
System.out.println("恭喜你,你抽中的奖金为:"+product2);
}
}
运行结果:
恭喜你,你抽中了:小米手机
================
恭喜你,你抽中的奖金为:20000
2.泛型类派生子类
1.子类也是泛型类,子类和父类的泛型类型要一致
class ChildGeneric<T> extends Generic<T>
//父类
public class Parent <E>{
private E value;
public E getValue() {
return value;
}
public void setValue(E value) {
this.value = value;
}
}
//子类
public class ChildFirst<T> extends Parent<T>{
@Override
public T getValue() {
return super.getValue();
}
}
public class Test01 {
public static void main(String[] args) {
ChildFirst<String> stringChildFirst = new ChildFirst<>();
stringChildFirst.setValue("java");
String s = stringChildFirst.getValue();
System.out.println(s); //java
}
}
2.子类不是泛型类,父类要明确泛型的数据类型
class ChildGeneric extends Generic<String>
//泛型类派生子类,如果子类不是泛型类,那么父类要明确数据类型
public class ChildSecond extends Parent<Integer> {
@Override
public Integer getValue() {
return super.getValue();
}
@Override
public void setValue(Integer value) {
super.setValue(value);
}
}
public class Test01 {
public static void main(String[] args) {
ChildFirst<String> stringChildFirst = new ChildFirst<>();
stringChildFirst.setValue("java");
String s = stringChildFirst.getValue();
System.out.println(s); //java
ChildSecond childSecond = new ChildSecond();
childSecond.setValue(520);
Integer value = childSecond.getValue();
System.out.println(value); //520
}
}
3.泛型接口
1.泛型接口的定义语法:
interface 接口名称 <泛型标识,泛型标识,…> {
泛型标识 方法名();
.....
}
2.泛型接口的使用
- 实现类也是泛型类,实现类和接口的泛型类型要一致
public interface Generator<T> {
T getKey();
}
/**
* 泛型接口的实现类,是一个泛型类
* 那么要保证实现接口的泛型类泛型标识,包含泛型接口的泛型标识
* @param <T>
* @param <E>
*/
public class Pair<T,E> implements Generator<T>{
private T key;
private E value;
public Pair(T key, E value) {
this.key = key;
this.value = value;
}
@Override
public T getKey() {
return key;
}
public E getValue() {
return value;
}
}
public class Test02 {
public static void main(String[] args) {
Apple apple = new Apple();
String key = apple.getKey();
System.out.println(key);
System.out.println("============");
Pair<String,Integer> pair = new Pair<>("java",100);
String key1 = pair.getKey();
Integer key2 = pair.getValue();
System.out.println(key1+"="+key2); //java=100
}
}
- 实现类不是泛型类,接口要明确数据类型
//实现泛型接口的类,不是泛型类,需要明确实现泛型接口的数据类型.
public class Apple implements Generator<String>{
@Override
public String getKey() {
return "hello,generator";
}
}
public class Test02 {
public static void main(String[] args) {
Apple apple = new Apple();
String key = apple.getKey();
System.out.println(key); //hello,generator
}
}
3.泛型方法
1.泛型类与泛型方法的区别:
- 泛型类,是在实例化类的时候指明泛型的具体类型。(强制要求成员方法返回的泛型必须遵从类的泛型)
- 泛型方法,是在调用方法的时候指明泛型的具体类型。(调用方法的时候独立指定的)
2.语法:
修饰符 <T,E, ...> 返回值类型 方法名(形参列表) {
方法体...
}
3.说明:
- public与返回值中间< T >非常重要,可以理解为声明此方法为泛型方法。
- 只有声明了< T >的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
- < T >表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
- 与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
4.与泛型类的案例的对比:
import java.util.ArrayList;
import java.util.Random;
/*
抽奖器
*/
public class ProductGetter<T> {
Random random = new Random();
//奖品
private T product;
//奖品池
ArrayList<T> list = new ArrayList<>();
//添加奖品
public void addProduct(T t){
list.add(t);
}
//抽奖
public T getProduct() {
T product = list.get(random.nextInt(list.size()));
return product;
}
/**
* 定义泛型方法
* @param list 参数
* @param <E> 泛型标识,具体类型,由调用方法的时候来指定。
* @return
*/
public <E> E getProduct(ArrayList<E> list){
return list.get(random.nextInt(list.size()));
}
}
import java.util.ArrayList;
public class Test03 {
public static void main(String[] args) {
ProductGetter<Integer> integerProductGetter = new ProductGetter<>();
int[] intProducts = {5000,6000,8000,20000};
for (int i = 0; i < intProducts.length; i++) {
integerProductGetter.addProduct(intProducts[i]);
}
//泛型类成员方法的调用
Integer product = integerProductGetter.getProduct();
System.out.println(product+"\t"+product.getClass().getSimpleName());
System.out.println("============");
ArrayList<String> stringArrayList = new ArrayList<>();
stringArrayList.add("macbook");
stringArrayList.add("iphone");
stringArrayList.add("robot");
//泛型方法的调用,类型是通过调用方法的时候来指定的。
String product1 = integerProductGetter.getProduct(stringArrayList);
System.out.println(product1+"\t"+product1.getClass().getSimpleName());
System.out.println("============");
ArrayList<Integer> integerArrayList = new ArrayList<>();
integerArrayList.add(10000);
integerArrayList.add(20000);
integerArrayList.add(30000);
Integer product2 = integerProductGetter.getProduct(integerArrayList);
System.out.println(product2+"\t"+product2.getClass().getSimpleName());
}
}
运行结果:
5000 Integer
============
robot String
===========
30000 Integer
5.静态泛型方法:
普通的成员方法如果采用了类的泛型,那么这个方法将不能被声明为静态的。
/**
* 静态的泛型方法,采用多个泛型类型
* @param t
* @param e
* @param k
* @param <T>
* @param <E>
* @param <K>
*/
public static <T,E,K> void printType(T t, E e, K k) {
System.out.println(t + "\t" + t.getClass().getSimpleName());
System.out.println(e + "\t" + e.getClass().getSimpleName());
System.out.println(k + "\t" + k.getClass().getSimpleName());
}
调用多个泛型类型的静态泛型方法:
ProductGetter.printType(100,"java",true);
运行结果:
100 Integer
java String
true Boolean
6.泛型方法与可变参数(可变参数本质上是数组)
public <E> void print(E... e){
for (E e1 : e) { //增强for
System.out.println(e);
}
}
/**
* 泛型可变参数的定义
* @param e
* @param <E>
*/
public static <E> void print(E... e){
for (int i = 0; i < e.length; i++) { //for
System.out.println(e[i]);
}
}
调用:
ProductGetter.print("java","python","golang");
运行结果:
java
python
golang
7.泛型方法总结:
- 泛型方法能使方法独立于类而产生变化。(最重要的特点)
- 如果static方法要使用泛型能力,就必须使其成为泛型方法。
4.类型通配符
1.类型通配符的定义:
- 类型通配符一般是使用”?”代替具体的类型实参。
- 所以,类型通配符是类型实参,而不是类型形参。
2.类型通配符的上限:
- 语法:
类/接口<? extends 实参类型>
要求该泛型的类型,只能是实参类型,或实参类型的子类类型。
采用上限通配符的集合不能够填充元素。
3.类型通配符的下限
- 语法:
类/接口<? super 实参类型>
要求该泛型的类型,只能是实参类型,或实参类型的父类类型。
采用下限通配符的集合可以填充子类类型,但是不保证元素数据类型的约束要求。
遍历的时候直接为Object类型接收。
- 使用:
TreeSet的源码:
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
public class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
'}';
}
}
public class Cat extends Animal{
public int age;
public Cat(String name, int age) {
super(name);
this.age = age;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class MinCat extends Cat{
public int level;
public MinCat(String name, int age, int level) {
super(name, age);
this.level = level;
}
@Override
public String toString() {
return "MinCat{" +
"name='" + name + '\'' +
", age=" + age +
", level=" + level +
'}';
}
}
import java.util.Comparator;
import java.util.TreeSet;
public class Test {
public static void main(String[] args) {
//TreeSet采用的是下限通配符,所以不能是comparator3
TreeSet<Cat> treeSet = new TreeSet<>(new comparator2());
//可以指定一个比较器。
//按照年龄大小来比较。
//如果是comparator1将按照name的首字母来比较。
treeSet.add(new Cat("Tom",6));
treeSet.add(new Cat("Bob",4));
treeSet.add(new Cat("Jim",5));
for (Cat cat : treeSet) {
System.out.println(cat);
}
}
}
//定义三个外部比较器
class comparator1 implements Comparator<Animal>{
@Override
public int compare(Animal o1, Animal o2) {
return o1.name.compareTo(o2.name);
}
}
class comparator2 implements Comparator<Cat>{
@Override
public int compare(Cat o1, Cat o2) {
return o1.age-o2.age;
}
}
class comparator3 implements Comparator<MinCat>{
@Override
public int compare(MinCat o1, MinCat o2) {
return o1.level-o2.level;
}
}
运行结果:
Cat{name='Bob', age=4}
Cat{name='Jim', age=5}
Cat{name='Tom', age=6}
5.类型擦除
1.概念:
泛型是Java 1.5版本才引进的概念,在这之前是没有泛型的,但是泛型代码能够很好地和之前版本的代码兼容。那是因为,泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除掉,我们称之为——类型擦除。
2.分类:
- 无限制类型擦除
- 有限制类型擦除(按照上限类型来擦除,把T转换为上限类型)
- 擦除方法中类型定义的参数
- 桥接方法
6.泛型与数组
1.泛型数组的创建:
- 可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象
ArrayList<String>[] listArr = new ArrayList<>(5); //会报错
ArrayList<String>[] listArr = new ArrayList[5];
用泛型类型创建数组引用,而对象不能够采用泛型了,采用原始数组赋值给引用listArr,操作的时候就可以用listArr操作。
- 可以通过java.lang.reflect.Array的**newInstance(Class,int)**创建T[]数组。
import java.lang.reflect.Array;
public class Fruit <T>{
private T[] array; //不能够直接new,类型不确定
//构造方法
public Fruit(Class<T> clz,int length){
//通过Array.newInstance创建泛型数组
array = (T[])Array.newInstance(clz,length);
}
/**
* 填充数组
* @param index
* @param item
*/
public void put(int index ,T item){
array[index] = item;
}
/**
* 获取数组元素
* @param index
* @return
*/
public T get(int index){
return array[index];
}
public T[] getArray(){
return array;
}
}
import java.util.Arrays;
public class Test02 {
public static void main(String[] args) {
Fruit<String> fruit = new Fruit<>(String.class,3);
fruit.put(0,"西瓜");
fruit.put(1,"苹果");
fruit.put(2,"香蕉");
System.out.println(Arrays.toString(fruit.getArray()));
String s1 = fruit.get(2);
System.out.println(s1);
}
}
运行结果:
[西瓜, 苹果, 香蕉]
香蕉
7.泛型和反射
反射常用的泛型类:
- Class< T >
- Constructor< T >
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
import java.lang.reflect.Constructor;
public class Test03 {
public static void main(String[] args) throws Exception {
Class<Person> personClass = Person.class;
Constructor<Person> constructor = personClass.getConstructor();
Person person = constructor.newInstance();
}
}