泛型的引出
测试代码
package com.pero.generic;
import java.util.ArrayList;
/**
* 在ArrayList中添加3个Dog对象
* Dog对象含有name和age,并输出name和age
*
* @author Pero
* @version 1.0
*/
@SuppressWarnings({"all"})
public class Test {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add(new Dog("小灰", 2));
arrayList.add(new Dog("小白", 3));
arrayList.add(new Dog("小黑", 5));
arrayList.add(new Cat("小花",4));
for (Object object : arrayList) {
Dog dog = (Dog) object; //遇到Cat类的小花时,报错ClassCastException
System.out.println(dog);
}
}
}
class Dog {
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
class Cat {
private String name;
private int age;
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
问题分析
1)不能对加入集合的数据类型进行约束,不安全!
2)便利的时候需要对类型转换,如果集合中的数据量比较大,会影响效率;
使用泛型的好处
1)编译时,检查添加元素的类型提高安全性;
2)减少了类型转换提高效率;
不使用泛型时:Dog -加入-> Object -取出-> Dog;添加进ArrayList中会先转成Object,取出时需要转成Dog,才能调用Dog的方法;
使用泛型时:Dog -加入-> Dog -取出-> Dog;存放和取出都是Dog,不需要在转型。
3)不再提示编译警告;
添加泛型之后代码
package com.pero.improve;
import java.util.ArrayList;
/**
* @author Pero
* @version 1.0
*/
public class Generic_ {
public static void main(String[] args) {
//ArrayList<Dog>表示存放到ArrayList集合集合中的元素时Dog类型
//编译器如果发现存放的类型不满足条件就会报错
//在遍历时,直接取出Dog类型,而不是Object类型
ArrayList<Dog> arrayList = new ArrayList<Dog>();
arrayList.add(new Dog("小灰", 2));
arrayList.add(new Dog("小白", 3));
arrayList.add(new Dog("小黑", 5));
//arrayList.add(new Cat("小花",4)); //编译器发现存放的类型不满足条件,报错
for (Dog dog :arrayList) {
System.out.println(dog.getName()+dog.getAge());
}
}
}
基本介绍
1)泛型又称为参数化类型,是jdk5.0出现的新特性,解决数据类型的安全问题;
2)在类声明或实例化时指定需要的具体类型;
3)Java泛型可以保证如果程序在编译时没有发出警告,则运行时就不会产生;
4)泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。
package com.pero.generic;
import java.util.Objects;
/**
* @author Pero
* @version 1.0
*/
@SuppressWarnings({"all"})
public class Generic01 {
public static void main(String[] args) {
Person<String, Integer> person = new Person<String, Integer>("jack",28);
//new Person<String,Integer>(100,30); //此时就会报错,在编译时就已经明确了参数的类型规定
person.show(); //class java.lang.String
}
}
//在类声明时定义泛型,通过一个标识符表示类中某个属性的类型,
// 创建Person对象时确定泛型类型(比如Person<String,Integer>,
// String类型替换了Person类的E,Integer类型替换了Person类的T),
// 在编译期间就确定E的类型!
class Person<E,T>{
E name; //规范name的类型,在创建对象时明确类型
T age; //规范age的类型,在创建对象时明确类型
public Person(E name, T age) {
this.name = name;
this.age = age;
}
public void show(){
System.out.println(name.getClass());
}
public E getName() {
return name;
}
public void setName(E name) {
this.name = name;
}
public T getAge() {
return age;
}
public void setAge(T age) {
this.age = age;
}
public E e(){
return name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person<?, ?> person = (Person<?, ?>) o;
return Objects.equals(name, person.name) && Objects.equals(age, person.age);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Person{" +
"name=" + name +
", age=" + age +
'}';
}
}
泛型的声明
interface 接口<T>{}和class 类<K,V>{}
例如:List,ArrayList
说明:
1)其中K,T,V并不代表值,而是表示类型;
2)任意字母都可以作为泛型的表示,通常用T(Type)。
3)要在类名后面指定类型参数的值(类型):
①List<String> strList = new ArrayList<String>();
②Iterator<Customer> iterator = customer.iterator();
package com.pero.generic;
import java.util.*;
/**
* 创建三个学生对象
* 放到HashMap中,要求Key是String name,Value就是学生对象
* 使用两种方式遍历
*
* @author Pero
* @version 1.0
*/
public class GenericExercise {
public static void main(String[] args) {
//3个学生对象
Student<String> jack = new Student<String>("jack");
Student<String> tom = new Student<String>("tom");
Student<String> lucy = new Student<String>("lucy");
//HashMap
HashMap<String, Student> hashMap = new HashMap<String, Student>();
hashMap.put("jack", jack);
hashMap.put("tom", tom);
hashMap.put("lucy", lucy);
//先取出EntrySet
/*
public Set<Map.Entry<K(替换成String),V(替换成Person)>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
*/
Set<Map.Entry<String, Student>> entries = hashMap.entrySet();
//迭代器遍历
/* //EntrySet类是HashMap的内部类
final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
//entries.iterator()会调用该方法,<K(替换成String),V(替换成Person)>
public final Iterator<Map.Entry<K,V>> iterator() {
return new EntryIterator();
}
}
*/
Iterator<Map.Entry<String, Student>> iterator_ = entries.iterator();
while (iterator_.hasNext()) {
Map.Entry<String, Student> entry = iterator_.next();
System.out.println(entry.getKey() + "-" + entry.getValue());
}
//遍历增强for遍历
for (Object object : entries) {
Map.Entry entry = (Map.Entry) object;
System.out.println(entry.getKey() + "-" + entry.getValue());
}
//HashSet
HashSet<Student> students = new HashSet<Student>();
students.add(jack);
students.add(tom);
students.add(lucy);
//迭代器遍历
Iterator<Student> iterator = students.iterator();
while (iterator.hasNext()) {
Student student = iterator.next();
System.out.println(student);
}
//增强for遍历
for (Student student : students) {
System.out.println(student);
}
}
}
class Student<T> {
private T name;
public Student(T name) {
this.name = name;
}
public T getName() {
return name;
}
public void setName(T name) {
this.name = name;
}
@Override
public String toString() {
return "Student:" + name;
}
}
泛型使用细节
1)interface List <T>{},public class HashSet<E>{}...等
T,E只能是引用类型,不能是基本数据类型
2)在指定泛型具体类型后,可以传入该类型或者其子类类型
3)泛型使用形式:
①List<Integer> list1 = new ArrayList<Integer>();
②List<Integer> list2 = new ArrayList<>();
4)List list3 = new ArrayList();默认泛型<E>是Object;
package com.pero.generic;
import java.util.ArrayList;
import java.util.List;
/**
* @author Pero
* @version 1.0
*/
public class GenericDetail {
public static void main(String[] args) {
//给泛型指定数据类型时,要求是引用类型不能是基本数据类型
List<Integer> list = new ArrayList<Integer>();
//List<int> list1 = new ArrayList<int>(); //编译报错
//在指定泛型具体类型后,可以传入该类型或者其子类类型
List<Animal> animals = new ArrayList<Animal>();
animals.add(new Animal()); //可以添加泛型规定的类型
animals.add(new Tiger()); //可以添加泛型规定类型的子类型
//animals.add(new Persons()); //不能添加
//泛型使用形式
List<Integer> list1 = new ArrayList<Integer>();
ArrayList<Integer> integers = new ArrayList<Integer>();
//简写(日常使用,编译器会进行类型推断)
ArrayList<Integer> integers1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
//默认泛型是Object
ArrayList arrayList = new ArrayList();
//等价于ArrayList<Object> arrayList = new ArrayList<Object>();
Persons persons = new Persons();
/* 上面的对象创建,相当于其类如下:
class Persons{
Object name;
public Persons(){};
public Persons(Object name){
this.name = name;
}
}
*/
}
}
class Animal {}
class Tiger extends Animal{}
class Persons<E>{
E name;
public Persons(){};
public Persons(E name){
this.name = name;
}
}
练习代码
package com.pero.generic;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
/**
* 定义Employee类
* 1)该类包含:private成员变量name、salary、birthday,其中birthday为MyData类的对象;
* 2)为每一个属性定义getter和setter方法;
* 3)重写toString方法,输出name、salary、birthday;
* 4)MyDate类包含:private成员变量month,day,year;并为每一个属性定义getter和setter方法;
* 5)创建该类的3个对象,并把这些对象放入ArrayList集合中(ArrayList需要用泛型定义),
* 对集合中的元素进行排序,并遍历输出(排序方法:调用ArrayList的sort()方法,传入Comparator对象【使用泛型】),
* 先按照name排序,如果name相同,则按照生日日期排序。
*
* @author Pero
* @version 1.0
*/
@SuppressWarnings({"all"})
public class GenericHomework {
public static void main(String[] args) {
Employee jack = new Employee("jack", 2300, new MyDate(1995, 12, 1));
Employee tom = new Employee("tom", 3300, new MyDate(1996, 1, 20));
Employee lucy = new Employee("lucy", 4300, new MyDate(1997, 1, 28));
ArrayList<Employee> employees = new ArrayList<Employee>();
employees.add(jack);
employees.add(tom);
employees.add(lucy);
employees.add(new Employee("smith",3600,new MyDate(2002,3,2)));
employees.add(new Employee("smith",5900,new MyDate(2002,3,1)));
employees.add(new Employee("jack",8800,new MyDate(1996,1,20)));
employees.sort(new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
if (!(o1 instanceof Employee && o2 instanceof Employee)) {
System.out.println("类型不匹配");
return 0;
}
int i = o1.getName().compareTo(o2.getName()); //名字排序
if (i != 0) {
return i;
} else {
return o1.getMyDate().minDate(o2.getMyDate()); //调用年龄比较方法,早出生的放在前面
}
}
});
Iterator<Employee> iterator = employees.iterator();
while (iterator.hasNext()) {
Employee employee = iterator.next();
System.out.println(employee);
}
}
}
class Employee {
private String name;
private double salary;
private MyDate myDate;
public Employee(String name, double salary, MyDate myDate) {
this.name = name;
this.salary = salary;
this.myDate = myDate;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public MyDate getMyDate() {
return myDate;
}
public void setMyDate(MyDate myDate) {
this.myDate = myDate;
}
@Override
public String toString() {
return "name='" + name + ", salary=" + salary + ", myDate=" + myDate;
}
}
class MyDate {
private int year;
private int month;
private int day;
public MyDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
@Override
public String toString() {
return year + "-" + month + "-" + day;
}
public int minDate(MyDate myDate) {
int yearMinus = this.getYear() - myDate.getYear();
int monthMinus = this.getMonth() - myDate.getMonth();
int dayMinus = this.getDay() - myDate.getDay();
if (yearMinus != 0) {
return yearMinus;
}
if (monthMinus != 0) {
return monthMinus;
}
return dayMinus;
}
}
自定义泛型类
基本语法
class 类名 <T,R,...>{
成员
}
注意细节
1)普通成员可以使用泛型(属性、方法);
2)使用泛型的数组,不能初始化;
3)静态方法中不能使用类的泛型;
4)泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型);
5)如果在创建对象时,没有指定类型,默认为Object;
package com.pero.customgeneric;
/**
* 自定义泛型类
*
* @author Pero
* @version 1.0
*/
public class CustomGeneric {
public static void main(String[] args) {
}
}
//Tiger后面有泛型,Tiger称为自定义泛型类
//一般是单个的大写字母作为泛型的标识符,例如:R,S,T...
//泛型的标识符可以有多个
class Tiger<R,S,T>{
String name;
R r;
S s;
T t;
//数组在new时不能确定T的类型,无法在内存中分配空间
T[] ts;
//T[] ts = new T[8]; //可以声明,不能初始化
//静态属性和方法中不能使用类的泛型,静态方法是和类相关的,在类加载时对象还没有创建,
// 如果静态方法和静态属性中使用泛型,泛型不能确定,JVM就无法完成初始化;
// private static T t1;
// public static void s(S s){
//
// }
public Tiger(String name, R r, S s, T t) {
this.name = name;
this.r = r;
this.s = s;
this.t = t;
}
//方法使用泛型,返回类型可以是泛型
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public R getR() {
return r;
}
public void setR(R r) {
this.r = r;
}
public S getS() {
return s;
}
public void setS(S s) {
this.s = s;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
练习代码
package com.pero.customgeneric;
/**
* @author Pero
* @version 1.0
*/
public class Test_ {
public static void main(String[] args) {
MyGeneric<Double, String, Integer> john = new MyGeneric<>("john");
john.setR(10.9);
//john.setR("jack"); setR(R r)方法内的参数必须是R泛型类型的,这里是Double类型
System.out.println("john:"+john);
//这里相当于MyGeneric<Object,Object,Object> myGeneric = new MyGeneric<>();
MyGeneric myGeneric = new MyGeneric("john~~");
//setR(R r)方法类泛型的类型现在是Object类,"yy"是String类,
// String类是Object的子类,所以可以传进去
myGeneric.setT("yy");
System.out.println("myGeneric:"+myGeneric);
}
}
class MyGeneric<R,S,T>{
R r;
S s;
T t;
public MyGeneric(S s){
this.s = s;
}
public MyGeneric(R r, S s, T t) {
this.r = r;
this.s = s;
this.t = t;
}
public R getR() {
return r;
}
public void setR(R r) {
this.r = r;
}
public S getS() {
return s;
}
public void setS(S s) {
this.s = s;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
自定义泛型接口
基本语法
interface 接口名<R,S,T...>{
}
注意细节
1)接口中,静态成员不能使用泛型;
2)泛型接口的类型,在继承接口或者实现接口时确定;
3)没有指定类型,默认为Object。
package com.pero.customgeneric;
/**
* @author Pero
* @version 1.0
*/
public class CustomInterfaceGeneric {
public static void main(String[] args) {
}
}
//在继承接口时指定泛型接口的类型
interface IA extends IUsb<String,Double>{
}
//在类实现接口时,接口所指定的泛型类型,在类中会直接进行替换
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) {
} //必须实现接口的所有抽象方法,否则编译器报错
}
//在实现接口时,直接指定泛型接口的类型
//类中实现接口方法时,泛型会直接进行替换
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) {
}
}
//自定义泛型接口
interface IUsb<U,R>{
int n = 10;
//U u; //接口中的成员都是静态属性的,不能使用泛型声明成员
//普通方法中,可以使用接口泛型
R get(U u);
void hi(R r);
void run(R r1,R r2,U u1,U u2);
//在jdk8中,可以在接口中,使用默认方法,也是可以使用泛型
//default是在java8中引入的关键字,在接口内部包含了一些默认的方法实现,
//从而使得接口在进行扩展的时候,不会破坏与接口相关的实现类代码。
default R method(U u){
return null;
}
}
//如果没有指定泛型类型,默认用Object替换泛型
//如果不明确泛型定义什么都不写,则会提醒警告
class CC implements IUsb<Object,Object>{
@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) {
}
}
自定义泛型方法
基本语法
修饰符 <R,S,T...>返回类型 方法名(参数列表){
}
注意细节
1)泛型方法,可以定义在普通类中,也可以定义在泛型类型中;
)当泛型方法被调用时,类型会确定;
3)public void eat(E e){},修饰符后没有<R,S,T...>,方法不是泛型方法,而是使用了泛型;
package com.pero.customgeneric;
/**
* @author Pero
* @version 1.0
*/
public class CustomMethodGeneric {
public static void main(String[] args) {
Car car = new Car();
car.fly("宝马",2330); //当调用方法时,传入参数,编译器就会确定类型
System.out.println("=================================");
car.fly(25330,2210);
Fish<String, Integer> stringIntegerFish = new Fish<>();
stringIntegerFish.eat("jack",126.6,'s');
}
}
//泛型方法,可以定义在普通类中,也可以定义在泛型类中
//普通类
class Car {
public void run() { //普通方法
}
//<T,V>就是泛型,提供给fly()方法使用
public <T, V> void fly(T t, V v) { //泛型方法
System.out.println(t.getClass());
System.out.println(v.getClass());
}
//使用泛型的普通方法,普通类中不能这样使用,编译报错(S s)
// public void swimming(S s){ //没有声明S泛型,无法找到
//
// }
}
//泛型类
class Fish<T,V>{
public void run(){ //普通方法
}
//泛型方法,可以使用类声明的泛型,也可以使用方法自己定义的泛型
public <R,S> void eat(T t,R r,S s){
System.out.println(t.getClass());
System.out.println(r.getClass());
System.out.println(s.getClass());
}
//使用泛型的普通方法,不是泛型方法
//使用类定义声明的泛型T
public void swimming(T t){
}
}
练习代码
package com.pero.customgeneric;
/**
* @author Pero
* @version 1.0
*/
public class CustomMethodGenericExercise {
public static void main(String[] args) {
Apple<String, Integer, Double> apple = new Apple<>();
apple.fly(10);
apple.fly(new Dog());
}
}
class Apple<T,R,M>{ //泛型类
public <E> void fly(E e){ //泛型方法
System.out.println(e.getClass().getSimpleName());
}
//public void eat(U u){} //查找不到泛型U,没有声明报错
public void run(M m){}
}
class Dog{}
泛型的继承和通配符
1)泛型不具备继承性;//List<Object> list = new ArrayList<String>();这样写错误
2)<?>:支持任意泛型类型;
3)<? entends A>支持A类以及A的子类,规定了泛型类型的范围上限
4)<? super A>支持A类以及A的父类,不限于直接父类,规定了泛型的下限;
package com.pero.customgeneric;
import java.util.ArrayList;
import java.util.List;
/**
* @author Pero
* @version 1.0
*/
public class GenericExtends {
public static void main(String[] args) {
Object object = new String("pero");
//泛型没有继承性
//List<Object> list = new ArrayList();
List<Object> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
List<Animal> list3 = new ArrayList<>();
List<Monkey> list4 = new ArrayList<>();
List<Person> list5 = new ArrayList<>();
//printCollection1(可以接受任意类型)
printCollection1(list1);
printCollection1(list2);
printCollection1(list3);
printCollection1(list4);
printCollection1(list5);
//printCollection2(只能接收Animal类型和Animal的子类类型)
//printCollection2(list1); //报错,Object类是Animal的父类
//printCollection2(list2); //报错,String类与Animal类不相关
printCollection2(list3);
printCollection2(list4);
printCollection2(list5);
//只能接收Animal类型和Animal的父类
printCollection3(list1);
//printCollection3(list2); //报错,String类与Animal类不相关
printCollection3(list3);
//printCollection3(list4); //报错,Monkey类是Animal类的子类
//printCollection3(list5); //报错,person类是Animal类的子类
}
//List<?>表示任意泛型类型都可以接收
public static void printCollection1(List<?> c) { //<?>通配符
for (Object o : c) {
System.out.println(o);
}
}
//? extends Animal 表示泛型可以接收Animal和Animal的子类类型,确定范围上限
public static void printCollection2(List<? extends Animal> c) {
for (Object o : c) {
System.out.println(o);
}
}
//? super Animal 表示泛型可以接收Animal和Animal 的父类,确定范围下限
public static void printCollection3(List<? super Animal> c) {
for (Object o : c) {
System.out.println(o);
}
}
}
class Animal {}
class Monkey extends Animal {}
class Person extends Monkey {}
JUnit单元测试类
为什么使用JUnit
1)一个类有很多功能代码需要测试,为了测试,就需要写到main方法中;
2)如果有多个功能代码测试,就需要来回注销,切换很麻烦;
3)如果可以直接运行一个方法,就方便很多,并且可以给出相关信息。
基本介绍
1)JUnit是一个Java语言的单元测试框架;
2)多数Java的开发环境都已经集成了Junit作为单元测试的工具
使用步骤:
1)在测试方法上一行添加@Test;
2)然后点击Alt+Enter,然后选择Add 'JUnit5.8.1' to classpath;
3)跟随引导添加即可。
package com.pero.junit_;
import org.junit.jupiter.api.Test;
/**
* @author Pero
* @version 1.0
*/
public class JUnit_ {
public static void main(String[] args) {
//传统方式
//new JUnit_().m1();
//new JUnit_().m2();
}
//JUnit单元测试,可以对方法进行单独测试
@Test
public void m1(){
System.out.println("m1()被调用");
}
@Test
public void m2(){
System.out.println("m2()被调用");
}
}