泛型
1.背景
- java推出泛型以前,程序员可以构建一个元素类型为object类型的集合,该集合能够存储任何的数据类型的对象,而在使用该集合的过程中,需要程序员明确知道每个元素的数据类型,否则很容易引发ClassCastException异常,那通过泛型我们就能够约束集合插入的类型,那么该集合的所有元素都必须是此类型,那么就不需要进行object类型转换。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
public static void main(String[] args) {
Student stu1 = new Student("马云",45,'男');
Student stu2 = new Student("马化腾",46,'男');
Student stu3 = new Student("董明珠",47,'女');
Student stu4 = new Student("章泽天",28,'女');
//<Student>泛型 用于约束集合中约束的类型
List<Student> list = new ArrayList<Student>();
list.add(stu1);
list.add(stu2);
list.add(stu3);
list.add(stu1);
list.add(1,stu4);
// list.add("张三丰");因为add(odject)类型可以插入任意类型,当定义集合的时候通过泛型约束了集合的类型是Student,所以只能插入Student类型的数据
Student student1 = (Student)list.get(1);
student1.showStudent();
System.out.println("================================");
for (int i = 0; i < list.size(); i++) {
Student student = (Student)list.get(i);
student.showStudent();
}
2.概念
- java泛型(generics)是java引入的一个新特性,泛型提供了编译时类型安全的监测机制,该机制允许我们在编译是检测到非法的数据类型结构。泛型的本质是参数化类型,通过<>指定参数来设定该数据结构的数据类型。
3.好处
- 类型安全,不会插入指定类型以外的类型
- 消除了强制类型的转换
4.泛型的类型
- 泛型类型可以定义在类上、父类上、子类上、方法上、参数上
- 泛型类型可以是任意字母来代替需要传入的参数
- 一般我们写法是:
- E -Element 代表集合中存放的元素
- T -Type 表示java类,包括基本的类和我们自定义的类
- K -Key 表示键,比如Map中的key
- V -Value 表示值
- N -Number 表示数值类型
- ? 代表不确定java类型
5.泛型类
1.泛型类定义
-
语法
类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>();
java7之后也可以这样写
类名<具体的数据类型> 对象名 = new 类名<>();
-
泛型类特征
- 泛型类,如果没有指定具体的数据类型,那么默认的类型是object类型
- 泛型的类型参数只能是引用类型,不能是基本类型
- 泛型类型在逻辑上可以看成是多个不同类型,但实际上是相同类型
2.案例1
-
Generic类
/** * 泛型类的定义 * @param <T> 泛型标识--类型形参 * T 创建对象的时候用来指定具体的数据类型 */ public class Generic <T>{ //T,是由外部使用类的时候来定义的 //传递是是什么类型T就是什么类型 private T key; public T getKey() { return key; } public void setKey(T key) { this.key = key; } public Generic(T key) { this.key = key; } public Generic() { } }
-
测试
public class Test { public static void main(String[] args) { // Generic generic = new Generic(); Generic<String> generic = new Generic<String>("abc"); String key1 = generic.getKey(); System.out.println("key1:"+key1); System.out.println("============================"); //在使用泛型类创建对象的时候才把泛型作为类型传递进去,那就能规定泛型类型中的T具体的类型 Generic<Integer> generic1 = new Generic<Integer>(100); Integer key2 = generic1.getKey(); System.out.println("key2:"+key2); //同一个泛型类,根据不同的数据类型创建的对象,本质上还是同一个类型 //(是在编译期该对象是Integer或String类型,但是在JVM运行期间变成了object类型(类型摘除的概念)) System.out.println(generic.getClass()==generic1.getClass()); } }
3.抽奖
-
奖品类
import java.util.ArrayList; import java.util.Random; public class ProductGetter <T>{ private T product; Random random = new Random(); //奖品池 ArrayList<T> list = new ArrayList<>(); //添加产品 public void addProduct(T t) { list.add(t); } //抽奖获取产品 public T getProduct() { product = list.get(random.nextInt(list.size())); return product; } }
-
测试
public class Test { public static void main(String[] args) { ProductGetter<String> stringProductGetter = new ProductGetter<String>(); String[] strProducts = {"苹果手机","华为手机","扫地机器人","咖啡机"}; //给奖品池填充奖品 for (int i = 0; i < strProducts.length; i++) { stringProductGetter.addProduct(strProducts[i]); } String product1 = stringProductGetter.getProduct(); System.out.println("恭喜你,你抽中了"+product1); System.out.println("============================"); ProductGetter<Integer> intProductGetter = new ProductGetter<Integer>(); int[] intProducts = {3000,5000,10000,200000}; //给奖品池填充奖品 for (int i = 0; i < intProducts.length; i++) { intProductGetter.addProduct(intProducts[i]); } int product2 = intProductGetter.getProduct(); System.out.println("恭喜你,你抽中了"+product2); } }
6.泛型子类
1.子类如果也是泛型类,子类和父类类型要一致
class Child<T> extends Parent<T>{}
-
父类
public class Parent <E>{ private E value; public Parent() { super(); } public Parent(E value) { super(); this.value = value; } public E getValue() { return value; } public void setValue(E value) { this.value = value; } }
-
子类
/** * 泛型类型的子类,泛型标识一定要和父类一致 * @param <T> */ public class Child<T> extends Parent<T>{ @Override public T getValue() { return super.getValue(); } @Override public void setValue(T value) { super.setValue(value); } }
-
测试
public class Test { public static void main(String[] args) { Child<String> child = new Child<String>(); child.setValue("abc"); String value = child.getValue(); System.out.println(value); } }
2.子类如果不是泛型类型,那么父类要明确泛型类型
class Child extends Parent<Integer>{}
-
父类
public class Parent <E>{ private E value; public Parent() { super(); } public Parent(E value) { super(); this.value = value; } public E getValue() { return value; } public void setValue(E value) { this.value = value; } }
-
子类
/** * 子类如果不是泛型类型,那么父类要明确泛型类型 * @author Administrator * */ public class ChildSecond extends Parent<Integer>{ @Override public Integer getValue() { // TODO Auto-generated method stub return super.getValue(); } @Override public void setValue(Integer value) { // TODO Auto-generated method stub super.setValue(value); } }
-
测试
public class Test { public static void main(String[] args) { ChildSecond childSecond = new ChildSecond(); //childSecond子类没有定义泛型,但是父类定义了是Integer类型 childSecond.setValue(123); Integer num = childSecond.getValue(); System.out.println(num); } }
7.泛型接口
1.子类如果也是泛型类,子类和父类类型要一致
-
接口
/** * 泛型接口 * @param <T> */ public interface Generator <T>{ T getKey(); }
-
实现类
/** * 泛型接口的实现类,一定要保证实现接口的泛型子类的泛型标识要包含泛型接口的泛型标识 * * @param <T> */ //public class Pair<T> implements Generator<T>{ //泛型类实现泛型接口,除了必须标识父类的泛型标识之外,还可以实现泛型的扩充 public class Pair<T,E> implements Generator<T>{ private T key; private E value; public Pair() { super(); } 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 Test { public static void main(String[] args) { Pair<String, Integer> pair = new Pair<String,Integer>("张三",1000); String key = pair.getKey(); Integer value = pair.getValue(); System.out.println(key + ":" + value); System.out.println("=============================="); Apple apple = new Apple(); String key1 = apple.getKey(); System.out.println(key1); } }
2.子类如果不是泛型类型,那么父类要明确泛型类型
-
接口
/** * 泛型接口 * @param <T> */ public interface Generator <T>{ T getKey(); }
-
实现类
/** * 子类如果不是泛型类型,那么父类要明确泛型类型 * @author Administrator * */ public class Apple implements Generator<String>{ @Override public String getKey() { // TODO Auto-generated method stub return "hello 泛型"; } }
-
测试
public class Test { public static void main(String[] args) { Pair<String, Integer> pair = new Pair<String,Integer>("张三",1000); String key = pair.getKey(); Integer value = pair.getValue(); System.out.println(key + ":" + value); System.out.println("=============================="); Apple apple = new Apple(); String key1 = apple.getKey(); System.out.println(key1); } }
8.泛型方法
-
ProductGetter类
import java.util.ArrayList; import java.util.Random; public class ProductGetter <T>{ private T product; Random random = new Random(); //奖品池 ArrayList<T> list = new ArrayList<>(); //添加产品 public void addProduct(T t) { list.add(t); } /** * 泛型成员方法(不是一种泛型方法) * @return */ //抽奖获取产品 public T getProduct() { //随机取到奖品集合中的一个元素 product = list.get(random.nextInt(list.size())); return product; } /** * 泛型方法 (在public和返回在中间定义的泛型才叫泛型方法) * @param <E> 泛型标识,具体类型由调用方法的时候来决定 * @param list 参数 * @return */ public <E> E getProduct(ArrayList<E> list) { return list.get(random.nextInt(list.size())); } /** * 定义静态的泛型方法,并且采用多个泛型类型 * @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()); } /** * String.... 代表多个String的参数 * 定义泛型个数可变的参数 * @param <E> * @param e */ public static<E> void print(E... e) { for (int i = 0; i < e.length; i++) { System.out.println(e[i]); } } }
-
测试
import java.util.ArrayList; public class Test { public static void main(String[] args) { ProductGetter<Integer> productGetter = new ProductGetter<Integer>(); ArrayList<String> strList = new ArrayList<>(); strList.add("笔记本"); strList.add("苹果手机"); strList.add("扫地机器人"); strList.add("华为手机"); String product1 = productGetter.getProduct(strList); System.out.println(product1+"\t"+product1.getClass().getSimpleName()); System.out.println("======================="); ArrayList<Integer> intList = new ArrayList<>(); intList.add(1100); intList.add(3000); intList.add(5000); intList.add(10000); //调用泛型方法<E>泛型标识,具体类型由调用方法的时候来决定的 Integer product2 = productGetter.getProduct(intList); System.out.println(product2+"\t"+product2.getClass().getSimpleName()); System.out.println("=========================="); ProductGetter.printType(100, "wu", true); ProductGetter.printType(100, 100.96, true); ProductGetter.printType(true, false, true); System.out.println("========================="); ProductGetter.print(1,24,5,4,5,4); ProductGetter.print(100, "wu", true); } }
9.类型通配符
类型通配符一般使用"?"代替具体的类型实参,当类型不明确具体的类型的时候可以用?来替代。
示例:
-
box类
public class Box<E> { private E first; public E getFirst() { return first; } public void setFirst(E first) { this.first = first; } }
-
测试类
public class Test { public static void main(String[] args) { Box<Number> box1 = new Box<Number>(); box1.setFirst(100); showBox(box1); Box<Integer> box2 = new Box<Integer>(); box2.setFirst(200); // showBox(box2);//报错,按照多态的思维创建子类对象后shouBox以父类类型接收是可以的,都是在泛型中不支持 //showBox方法中定义的是Box<Number> Number类型,所以只能是接收number类型实参 } private static void showBox(Box<Number> box1) { Number first = box1.getFirst(); System.out.println(first); } }
-
使用通配符
public class Test { public static void main(String[] args) { Box<Number> box1 = new Box<Number>(); box1.setFirst(100); showBox(box1); Box<Integer> box2 = new Box<Integer>(); box2.setFirst(200); showBox(box2); } //?是通配符,代表任意类型 private static void showBox(Box<?> box1) { Object first = box1.getFirst(); System.out.println(first); } }
10.类型通配符上限
//通配符上限? extends Number 只能是Number类型或者是Number类型的子类
private static void showBox(Box<? extends Number> box1) {
Object first = box1.getFirst();
System.out.println(first);
}
示例:
-
创建父类Animal类
public class Animal { }
-
创建子类Cat继承Animal
public class Cat extends Animal{ }
-
创建子类Mincat继承Cat
public class Mincat extends Cat{ }
-
测试类
import java.util.ArrayList; public class Test { public static void main(String[] args) { ArrayList<Animal> animals = new ArrayList<Animal>(); ArrayList<Cat> cats = new ArrayList<Cat>(); ArrayList<Mincat> minicat = new ArrayList<Mincat>(); showAnimal(cats); showAnimal(minicat); // showAnimal(animals);//报错:showAnimal(ArrayList<? extends Cat> list)设定参数只能是Cat子类或者Cat类型 } /** * 泛型通配符上限 * @param list */ private static void showAnimal(ArrayList<? extends Cat> list) { for (int i = 0; i < list.size(); i++) { Cat cat = list.get(i); System.out.println(cat); } } }
11.类型通配符下限
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<Animal> animals = new ArrayList<Animal>();
ArrayList<Cat> cats = new ArrayList<Cat>();
ArrayList<Mincat> minicat = new ArrayList<Mincat>();
showAnimal(cats);
// showAnimal(minicat);//报错:ArrayList<? super Cat> list是通配符下限.定义了参数只能是Cat或者Cat的父类类型
showAnimal(animals);
}
/**
* 泛型通配符下限,只能是Cat或者Cat的父类类型
* @param list
*/
private static void showAnimal(ArrayList<? super Cat> list) {
for (int i = 0; i < list.size(); i++) {
Object cat = list.get(i);
System.out.println(cat);
}
}
}
12.泛型数组
- 可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象
- 可以通过java.lang.reflect.Array的newInstance(class , int )创建T[]数组
示例:
-
自定义数组
import java.lang.reflect.Array; /** * 自定义泛型数组 * @param <T> */ public class Fruit<T> { // private T[] array =new T[3]; private T[] array; public Fruit(Class<T> clz,int length) { //可以通过java.lang.reflect.Array的newInstance(class , int )创建T[]数组 array = (T[])Array.newInstance(clz, length); } /** * 填充数组 * @param index 下标位置 * @param item 元素 */ public void put(int index,T item) { this.array[index]=item; } /** * 根据下标获取数组元素 * @param index 下标 * @return 返回的元素 */ public T get(int index) { return this.array[index]; } /** * 返回数组 */ public T[] getArray() { return this.array; } }
-
测试
import java.util.ArrayList; import java.util.Arrays; public class Test { public static void main(String[] args) { //可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象,可以分为下面两步去创建 // ArrayList<String>[] list = new ArrayList<String>[5]; //分为了两步创建了一个能够存储5个长度的String类型的数组 ArrayList[] list = new ArrayList[5]; ArrayList<String>[] listArr = list; //还可以这样创建泛型类型的数组 ArrayList<String>[] list1 = new ArrayList[5]; ArrayList<String> strlist = new ArrayList<String>(); strlist.add("abc"); ArrayList<Integer> intlist = new ArrayList<Integer>(); intlist.add(100); // list1[0] = intlist; list1[0] = strlist; String value = strlist.get(0); System.out.println("=============================="); //创建自定义泛型数组 Fruit<String> fruit = new Fruit<String>(String.class,3); fruit.put(0, "苹果"); fruit.put(1, "香蕉"); fruit.put(2, "哈密瓜"); System.out.println(Arrays.toString(fruit.getArray())); } }