标题
- 1. 泛型语法
- 1.1 泛型的引出
- 1.2 泛型介绍
- 1.3 泛型语法
- 1.4 泛型使用细节
- 1.5 练习题
- 2.自定义泛型
- 2.1自定义泛型-类
- 2.2自定义泛型-接口
- 2.3自定义泛型-方法
- 2.4练习题
- 3.范型继承和通配符
- 3.1JUnit单元测试框架
- 3.2练习题
1. 泛型语法
1.1 泛型的引出
- 传统方法不能对加入到集合中的数据类型进行约束;
- 对集合遍历的时候需要进行类型转换,效率低;
public class Generic01 {
public static void main(String[] args){
ArrayList arrayList = new ArrayList();
arrayList.add(new Dog("scott",4));
arrayList.add(new Dog("jack",5));
arrayList.add(new Dog("mary",6));
for(Object o : arrayList) {
Dog dog = (Dog) o;//获取对象的名字
System.out.println(dog.getName());
}
}
}
class Dog {
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
- 引出:new ArrayList<Dog>(),表示存放到ArrayList 集合中的元素是Dog类型
- 优点
- 如果编译器发现添加的类型不满足要求,就会报错,即编译时编译器会检查添加元素的类型,提高安全性;
- 遍历时,可以直接取出Dog类型,而不是Object类型,减少了类型转换的次数;
public class Generic01 {
public static void main(String[] args){
ArrayList arrayList = new ArrayList<Dog>();
arrayList.add(new Dog("scott",4));
arrayList.add(new Dog("jack",5));
arrayList.add(new Dog("mary",6));
//arrayList.add(new Cat("招财猫",3));
for(Dog dog : arrayList) {
System.out.println(dog.getName());
}
}
}
不使用泛型:放入到ArrayList会先转成Object,在取出时,再转换成Dog;
使用泛型:放入和取出时,不需要类型转换;
不使用泛型 | 使用泛型 |
---|---|
放入到ArrayList会先转成Object,在取出时,再转换成Dog; | 放入和取出时,不需要类型转换; |
1.2 泛型介绍
- 泛型是可以表示数据类型的一种数据类型;
- 泛型又称为参数化类型,是jdk5.0出现的新特性,可以解决数据类型的安全性问题;
- 在类声明或者实例化的时候只需要指定需要的具体类型即可;
- Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException的异常;同时,代码更简洁、健壮。
- public class ArrayList<E> ,相当于把Dog赋给了E,Dog->E
- 泛型的作用是: 可以在类声明时通过一个标识符表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。
- 参数的类型
- 类中方法返回值的类型
- 属性的类型
泛型 | Generic |
---|---|
参数的类型 | $1600 |
类中方法返回值的类型 | $12 |
属性的类型 | $1 |
- 在这里,E的类型一定要清楚的知道,可以通过getClass()方法得到;
- 注意:E具体的数据类型在定义Person对象时指定,即在编译期间就已确定类型;
1.3 泛型语法
- interface 接口<T> {} 和 class 类<K,V>{}
- 说明:
- T、K、V不代表值,而是表示类型;
- 任意字母都可以,常用T表示,T是Type的缩写
- 泛型的实例化:要在类名后面指定类型参数的值(类型),如:
- (1)List<String> stringList = new ArrayList<\String>();
- (2)Interator<\Dog> iterator = dog.iterator();
1.4 泛型使用细节
- interface List<T> {}, public class HashSet<E> {};
T,E只能是引用类型,不能用基本数据类型;(Type argument cannot be of primitive type)
- 在给泛型指定具体类型后,可以传入该类型或其子类类型;
- 泛型的简写形式:省略构造器处的泛型表示,编译器会进行类型推断;
List<Pig> list = new ArrayList<>();- 如果没有指定泛型,默认泛型是Object;
Map hashMap = new HashMap();
等价于
Map<Object, Object> hashMap = new HashMap<>();
1.5 练习题
改进后:封装后代码复用性更高;
2.自定义泛型
2.1自定义泛型-类
- 基本语法:class 类名<T,R…> {} 可以有多个泛型
- 普通成员可以使用泛型(属性、方法);
- 使用泛型的数组不能初始化;
- 静态成员中不能使用类的泛型;
- 泛型类的类型,是在创建对象时确定的;(因为在创建对象时,需要指定类型)
- 如果在创建对象时,没有指定类型,默认为Object;
public class CustomGeneric {
public static void main(String[] args) {
//T是Double,R是String,M是Integer
Tiger<Double, String, Integer> tiger = new Tiger<>("john~~");
tiger.setT(3.2);//OK
System.out.println(tiger);
Tiger tiger1 = new Tiger("john");
//tiger.setT("yy");因为T是Object,"yy"是 Object子类,类型不对
}
}
//1.Tiger后面有泛型,所以我们把Tiger 称为自定义泛型类
//2.T, R, M 泛型的标识符,一般是单个大写字母
//3.泛型的标识符可以有多个
//4.普通成员可以使用泛型 (属性、方法)
//5.使用泛型的数组,不能初始化
class Tiger<T, R, M> {
String name;
T t;//属性使用到泛型
M m;
R r;
public Tiger(String name) {
this.name = name;
}
//Type parameter 'T' cannot be instantiated directly
//T[] ts = new T[8];类型不能确定,无法分配空间,因为数组在new的时候不能确定T的类型,就无法在内存开辟空间
public Tiger(T t, M m, R r) {//构造器使用泛型
this.t = t;
this.m = m;
this.r = r;
}
//因为静态是和类相关的,在类加载时,对象还没有创建
//所以,如果静态方法和静态属性使用了泛型,JVM就无法完成初始化
/*static R r2;
public static void m1(M m) {//cannot be referenced from a static context
}*/
//方法使用泛型
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
public M getM() {
return m;
}
public void setM(M m) {
this.m = m;
}
public R getR() {//返回类型可以使用泛型
return r;
}
public void setR(R r) {//方法使用到泛型
this.r = r;
}
}
2.2自定义泛型-接口
基本语法:interface 接口名 <T, R…> {}
- 接口中静态成员也不能使用泛型;(和泛型类规定一样)
- 泛型接口的类型,在继承接口或者实现接口时确定;
- 如果没有指定类型,默认为Object;(不规范,建议指定<Object>)
- 方法参数列表中的泛型直接给值,类定义时的泛型给类型;
/**
* 泛型接口使用说明
* 1.接口中,静态成员不能使用泛型
* 2.泛型接口的类型是在继承接口或者实现接口时实现的
*/
//1.在继承接口时,指定泛型接口的类型
interface IA extends IUsb<String, Double> {
}
//当我们去实现IA接口时,因为IA在继承IUsb接口时,指定了U为String,R为Double
// 因此在实现IUsb接口方法时,使用String替换U,Double替换R
class AA implements IA {
@Override
public Double get(String s) {
return null;
}
@Override
public void hi(Double aDouble) {
}
@Override
public void run(Double r1, Double r2, String u1, String u2) {
}
}
//2.实现接口时,直接指定泛型接口的类型
//给U指定了Integer, 给R指定了Float
//所以当实现IUsb接口方法时,会使用Integer替换U,Float替换R
class BB implements IUsb<Integer, Float> {
@Override
public Float get(Integer integer) {
return null;
}
@Override
public void hi(Float aFloat) {
}
@Override
public void run(Float r1, Float r2, Integer u1, Integer u2) {
}
}
//3.没有指定泛型,就默认为Object
//等价于class CC implements IUsb<Object, Object> {}, 但是不规范
class CC implements IUsb {
@Override
public Object get(Object o) {
return null;
}
@Override
public void hi(Object o) {
}
@Override
public void run(Object r1, Object r2, Object u1, Object u2) {
}
}
interface IUsb<U, R> {//U=Integer, R=Float
//接口中普通方法,可以使用泛型
R get(U u);
int n = 10;
//U name = "3e";静态成员不能使用泛型
void hi(R r);
void run(R r1, R r2, U u1, U u2);
//在jdk8以后,可以在接口中使用默认方法,在默认方法中可以使用泛型
default R method(U u) {
return null;
}
}
2.3自定义泛型-方法
基本语法:修饰符[空格]<T, R> 返回类型 方法名(参数列表) {}
- 泛型方法,可以定义在普通类中,也可以定义在泛型类中;
- 当泛型方法被调用时,泛型类型就会确定,否则调用不起来;
- public void ear(E e) {},修饰符后面没有 <T, R…>, 那么eat方法就不是泛型方法,只是使用了泛型;
- 泛型方法可以使用类声明的泛型,也可以使用自己声明的泛型;
public class CustomMethodGeneric_ {
public static void main(String[] args) {
Car car = new Car();
//2. 当泛型方法被调用时,泛型类型就会确定,否则调用不起来;
car.fly("宝马",1200);//当调用方法时,传入参数,编译器就会确定类型
System.out.println("===========");
car.fly(2.2,1200);//当调用方法时,传入参数,编译器就会确定类型
System.out.println("===========");
//T:Sring, R:ArrayList
Fish<String, ArrayList> fish = new Fish<>();
//K:Float
fish.hello(new ArrayList(), 2.1f);
}
}
//1.泛型方法,可以定义在普通类中
class Car {//普通类
public void run() {//普通方法
}
//说明:泛型方法
// <T, R> 就是泛型
// 提供给fly方法使用的
public <T, R> void fly(T t, R r) {//泛型方法
System.out.println(t.getClass());//String
System.out.println(r.getClass());//Integer
}
}
//2.泛型方法,也可以定义在泛型类中
class Fish<T, R> {//泛型类
public void run() {//普通方法
}
public <U, M> void fly(U u, M m) {//泛型方法
}
//说明
// hi()方法不是一个泛型方法
// 只是hi()方法使用了类声明的泛型
public void hi(T e) {
}
//泛型方法,可以使用类声明的泛型,也可以使用自己声明的泛型
public <K> void hello(R r, K k) {
System.out.println(r.getClass());//ArrayList
System.out.println(k.getClass());//Float
}
}
2.4练习题
public class CustomMethodGenericExercise {
public static void main(String[] args) {
//T:String, R:Integer, M:Double
Apple<String, Integer, Double> apple = new Apple<>();
apple.fly(10);//泛型都是引用类型,所以10自动装箱,输出:Integer
apple.fly(new Dog());//输出:Dog
}
}
class Apple<T, R, M> {
public <E> void fly(E e) {
//.getClass():显示包名+类名;
//.getClass().getSimpleName():只显示类名
System.out.println(e.getClass().getSimpleName());
}
//public void eat(U u) {} //错误,U没有声明
public <U> void eat(U u) {} //改正
public void run(M m) {}
}
class Dog {}
3.范型继承和通配符
- 泛型不具备继承性;
List<Object> list = new ArrayList<String> ();//这样写是错误的 - 通配符起到约束作用;
通配符 | 作用 |
---|---|
List<?> c | 表示可以接收任意范型类型 |
List<? extends A> c | 表示可以接收A类或者A类的子类,规定泛型上限 |
List<? super A> c | 表示可以接收A类或者A类的父类(不限于直接父类),规定泛型下限 |
@SuppressWarnings({"all"})
public class GenericExtends {
public static void main(String[] args) {
//范型没有继承性
//List<Object> list = new ArrayList<String>();
//举例说明下面三个方法的使用;
List<Object> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
List<AA> list3 = new ArrayList<>();
List<BB> list4 = new ArrayList<>();
List<CC> list5 = new ArrayList<>();
//1.如果是 List<?> c, 可以接收任意的范型类型
printCollection1(list1);
printCollection1(list2);
printCollection1(list3);
printCollection1(list4);
printCollection1(list5);
//2.如果是 List<? extends AA> c, 可以接收AA或者AA的子类
//printCollection2(list1);
//printCollection2(list2);
printCollection2(list3);
printCollection2(list4);
printCollection2(list5);
//3.如果是 List<? super AA> c, 可以接收AA,AA的父类但不限于直接父类
printCollection3(list1);//正确
//printCollection3(list2);
printCollection3(list3);
//printCollection3(list4);
//printCollection3(list5);
}
public static void printCollection1(List<?> c) {
for (Object object : c) {//通配符,取出时就是Object
System.out.println(object);
}
}
public static void printCollection2(List<? extends AA> c) {
for (AA object : c) {
System.out.println(object);
}
}
//List<?> 表示任意范型类型都可以接收
//List<? extends AA> 表示的是上限,可以接收 AA或AA的子类
//List<? super AA> 表示的是下线,可以接收 AA或者AA的父类,不限于直接父类
public static void printCollection3(List<? super AA> c) {
for (Object object : c) {
System.out.println(object);
}
}
}
class AA {
}
class BB extends AA {
}
class CC extends BB {
}
3.1JUnit单元测试框架
shortcuts: Alt+Enter/option+Enter
可以直接运行一个方法,JUnit测试框架;
public class JUnit_ {
public static void main(String[] args) {
//new JUnit_().m1();
//new JUnit_().m2();
}
@Test
public void m1() {
System.out.println("m1()方法");
}
@Test
public void m2() {
System.out.println("m2()方法");
}
}
3.2练习题
public class Homework01 {
public static void main(String[] args) {
}
@Test
public void test() {
//这里我们给泛型T指定的类型是User
DAO<User> userDAO = new DAO<>();
userDAO.save("No1", new User(1, 22));
userDAO.save("No2", new User(2, 23));
userDAO.save("No3", new User(3, 23));
List<User> list = userDAO.list();
userDAO.update("No3", new User(3, 18));
list = userDAO.list();
userDAO.delete("No3");
list = userDAO.list();
User user = userDAO.get("No2");
System.out.println(list);
System.out.println(user.getAge());
}
}
class DAO<T> {
private Map<String, T> map = new HashMap<>();
public void save(String id, T entity) {
map.put(id, entity);
}
public T get(String id) {
return map.get(id);
}
public void update(String id, T entity) {
map.put(id, entity);
}
public List<T> list() {
List<T> arrayList = new ArrayList<>();
Set<String> keySet = map.keySet();
Iterator<String> iterator = keySet.iterator();
while (iterator.hasNext()) {
String key = iterator.next();
arrayList.add(get(key));
}
return arrayList;
}
public void delete(String id) {
map.remove(id);
}
}
class User {
private int id;
private int age;
public User(int id, int age) {
this.id = id;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "\nUser{" +
"id=" + id +
", age=" + age +
'}';
}
}