一、引入
1、泛型是什么
泛型 ,顾名思义就是 广泛的数据类型,也就是说什么数据类型都可以。一般来说,我们见到的泛型就是这个样子,用 T 表示。如下所示,在类名后方申明泛型 T,接着就可以在成员变量、方法中使用泛型了。其中我们在java 集合构架中以及以后将会广泛的应用到泛型。
首先告诉大家ArrayList就是泛型之一。
首先让我们来看一个例子,认识一下泛型的作用之一。
在ArrayList 集合遍历中,我们又是在处理时将会用到泛型,但若保存了不同的对象,在遍历时就将报类型转换错误:
package fanxing.com;
import java.util.ArrayList;
@SuppressWarnings({"all"})
public class fanxingTest {
public static void main(String[] args) {
// 泛型:
ArrayList<Dog> dogs = new ArrayList<Dog>();// 当我们这样写,表示ArrayList 集合中的元素只能是Dog 类型
// 如果加入的是Cat那编译器就会报错
dogs.add(new Dog("tom",6));
dogs.add(new Dog("小黄",10));
dogs.add(new Dog("招财",8));
// 在遍历使用get 和 set 方法时不用在向下转型
for (Dog dog :dogs) {
System.out.println(dog.getName()+"-"+dog.getAge());
}
}
}
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;
}
}
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;
}
}
以上代码中,如果我们不使用泛型而同时存放了Cat 类对象和 Dog类对象,那在遍历时就将出错
那为了解决这个问题就可以参考以上代码,使用泛型,在生成ArrayList 对象时指定他所存储对象的类型。
另外泛型当然不仅仅只是为了解决这个问题啦,等到后面的题目时你就能感受到它的神奇之处了。
下面我们将对泛型的写法和用法做一一讲解。
二、各种泛型定义及使用 ?
那么参照上一段代码,让我们来看看泛型可以怎样在集合中使用:
package fanxing.com;
import java.util.*;
@SuppressWarnings({"all"})
public class GenericTest01 {
public static void main(String[] args) {
// 使用泛型,在HashSet中放入三个学生对象
HashSet<Student> students = new HashSet<>();
students.add(new Student("jact",19));
students.add(new Student("tom",21));
students.add(new Student("jhon",18));
Iterator<Student> iterator = students.iterator();
while (iterator.hasNext()) {
Student next = iterator.next();
System.out.println(next.getName()+"-"+next.getAge());
}
// 用HashMap 存放 key 为name ,value 为学生对象
HashMap<String, Student> map = new HashMap<>();
map.put("jact",new Student("jact",19));
map.put("tom",new Student("tom",23));
map.put("majunyi",new Student("majunyi",20));
Set<Map.Entry<String, Student>> entries = map.entrySet();
Iterator<Map.Entry<String, Student>> iterator1 = entries.iterator();
while (iterator1.hasNext()) {
Map.Entry<String, Student> next = iterator1.next();
Student value = next.getValue();
System.out.println(value.getName()+"-"+value.getAge());
}
}
}
class Student{
private String name;
private int age;
public Student(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;
}
}
另外在对进一步了解之前让我们看看泛型的一些注意事项吧:
package fanxing.com;
import java.util.ArrayList;
import java.util.List;
// 泛型使用注意事项
@SuppressWarnings({"all"})
public class GenericTest02 {
public static void main(String[] args) {
// 注意: E 不能是基本数据类型,比如 int、float、double 等
// 1. 在给泛型指定具体类型以后,可以传入该类型或其子类对象
Pig<A> aPig = new Pig<A>(new A());
aPig.f(); // class fanxing.com.A
Pig<A> aPig1 = new Pig<A>(new B());
aPig1.f(); // class fanxing.com.B
// 2.泛型的使用形式
List<Integer> list1 = new ArrayList<Integer>();
// 在实际开发中可以简写,并且更为推荐下面一种写法
List<Integer> list2 = new ArrayList<>();
ArrayList<Pig> pigs = new ArrayList<>();
// 3. 如果是这样写 泛型默认是 Object 类
ArrayList arrayList = new ArrayList();//等价于 ArrayList<Object> arrayList = new ArrayList<>();
arrayList.add("majnuyi");//默认添加为 Object
Tige tige = new Tige();
/* 相当于:
class Tige{
Object e;
public Tige(){
}
public Tige(Object e) {
this.e = e;
}
}
*/
}
}
class Tige<E>{
E e;
public Tige(){
}
public Tige(E e) {
this.e = e;
}
}
class A{}
class B extends A{}
class Pig<E>{
E e;
public Pig(E e) {
this.e = e;
}
public void f(){
System.out.println(e.getClass());
}
}
1.泛型接口:
package fanxing.com;
// 泛型接口
public class Generic_interface {
public static void main(String[] args) {
}
}
/**
* 1. 接口中静态成员也不能使用泛型
* 2. 泛型接口的类型,在继承接口或者实现接口时确定,如果没有指定默认Object 类
*/
interface Usb<U,R>{
// 普通方法中,可以使用接口泛型
R get(U u);
void hi(R r);
void run(R r1,R r2,U u1,U u2);
// 在 jdk8 中,可以在接口中使用默认方法,也可以使用泛型
default R method(U u){
return null ;
}
}
// 当我们去实现IA 接口时,因为 IA 在继承Usb 接口时指定了U 为String ,T 为 Double
// 在实现Usb 接口中方法时,使用String 代替 U,用 Double 代替 T
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) {
}
@Override
public Double method(String s) {
return IA.super.method(s);
}
}
// 在继承接口时指定泛型
interface IA extends Usb<String,Double>{
}
/**
* 在实现接口时指定泛型接口的类型
* 给 U指定了 Integer, 给 T 指定了 Float
*/
class BB implements Usb<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) {
}
@Override
public Float method(Integer integer) {
return Usb.super.method(integer);
}
}
/** 不指定时默认为Object 类
class CC implements Usb{// 等价于 给 U 和 T 指定了 Object,不建议这种写法,建议下面的写法
}
*/
class CC implements Usb<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) {
}
@Override
public Object method(Object o) {
return Usb.super.method(o);
}
}
2.泛型方法:
package fanxing.com;
// 泛型方法
public class Generic_main {
public static void main(String[] args) {
Car car = new Car();
car.fly("majnuyi",5784);// 调用时传入参数,自动装箱,编译器,就会确定类型
car.fly(845,"546457");
Fish<String, Double> fish = new Fish<>();
System.out.println(fish.hi("zhanguhgds"));
}
}
// 泛型方法可以放在普通类中,也可以放在泛型类中
class Car{// 普通类
public void run(){
}
// 说明泛型方法
// 1. <T,R> 就是泛型
// 2. 是提供给fly使用
public <T,R> void fly(T t,R r){// 泛型方法
System.out.println(t+"-"+r);
}
}
class Fish<T,R>{
T t;
R r;
public void run(){
}
// 泛型方法
public <U,M> void eat(U u,M m){
}
/*
注意:
下面方法只是使用了泛型,并不是泛型方法
是 hi方法使用了类声明的泛型
另外 泛型方法不仅可以使用类声明的泛型,还可以使用泛型方法中声明的泛型
并且,普通方法使用泛型,必须是类声明的泛型
*/
public T hi(T t){
return t;
}
}
3.自定义泛型类:
package fanxing.com;
// 自定义泛型
@SuppressWarnings({"all"})
public class GenericTest04 {
public static void main(String[] args) {
Tiges<Double, String, Integer> Tige = new Tiges<Double, String, Integer>("jhon");
Tige.setT(10.87);
Tige.setM(68);
Tige.setR("majunyi");
System.out.println(Tige); // 输出 Tiges{name='jhon', r=majunyi, m=68, t=10.87}
// 以下代码也对,默认Object 类
Tiges tiges = new Tiges("tom");
tiges.setR("bfhjdrgh");
tiges.setT("zhang");
tiges.setM(3568);
System.out.println(tiges);// 输出 Tiges{name='tom', r=bfhjdrgh, m=3568, t=zhang}
}
}
// 1. Tiges 后面有泛型,所以我们将 Tiges 称为泛型类
// 2. T、R、M为泛型标识符,一般使用大写字母
// 3. 泛型标识符可以有多个
// 4. 普通成员可以是泛型(方法、属性)
// 5. 使用泛型数组时不能进行初始化
// 6. 静态方法中不能使用类的泛型
class Tiges<T,R,M>{
String name;
R r;
M m;
T t;
// T[] ts=new T[8]; 错误,因为数组类型无法确定下来,无法在内存中开辟空间
public Tiges(String name) {
this.name = name;
}
public Tiges( R r, M m, T t) {
this.r = r;
this.m = m;
this.t = t;
}
/*
因为静态是和类相关的,在类加载时,对象还没有被创建
所以,如果静态方法和静态属性中定义了泛型,JVM 就无法完成初始化
static R r2;
public static void f(M m){
}
*/
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 M getM() {
return m;
}
public void setM(M m) {
this.m = m;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
@Override
public String toString() {
return "Tiges{" +
"name='" + name + '\'' +
", r=" + r +
", m=" + m +
", t=" + t +
'}';
}
}
那么通过以上对泛型的了解,让我们来做一个间的泛型题目吧:
package fanxing.com;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
// 泛型练习
@SuppressWarnings({"all"})
public class GenericTest03 {
public static void main(String[] args) {
/*
定义Employee类,该类包括 private: neme,sal,birthday(MyDate)类,
其中 birthday 为 MyDate类包括: year,month,day,要求:
使用泛型 添加三个员工存入ArrayList 中,并调用sort 方法传入 Comparator 对象(使用泛型)
先按照name 排序,name相同则按照日期先后
最后遍历集合
*/
ArrayList<Employee> employees = new ArrayList<>();
employees.add(new Employee("tom",20000,new MyDate(2002,12,29)));
employees.add(new Employee("jact",30000,new MyDate(2001,10,7)));
employees.add(new Employee("tom",50000,new MyDate(2003,9,19)));
System.out.println(employees+"\n\n\n");
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());
// 如果 i!=0;则直接返回
if (i!=0){
return i;
}// name相同则按照日期先后
// ====因为下面是对日期的比较,代码较乱,不利于代码封装,因此 将日期比较交给 MyDate 类完成==
// 封装后,将来的可复用性和可维护性将大大提升
else {
return o1.getBrithday().compareTo(o2.getBrithday());
}
}
});
Iterator<Employee> iterator = employees.iterator();
while (iterator.hasNext()) {
Employee next = iterator.next();
System.out.println(next);
}
}
}
class Employee{
private String name;
private double Sal;
private MyDate brithday;
public Employee(String name, double sal, MyDate brithday) {
this.name = name;
Sal = sal;
this.brithday = brithday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSal() {
return Sal;
}
public void setSal(double sal) {
Sal = sal;
}
public MyDate getBrithday() {
return brithday;
}
public void setBrithday(MyDate brithday) {
this.brithday = brithday;
}
@Override
public String toString() {
return "\nEmployee{" +
"name='" + name + '\'' +
", Sal=" + Sal +
", brithday=" + brithday +
'}';
}
}
class MyDate implements Comparable<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 "MyDate{" +
"year=" + year +
", month=" + month +
", day=" + day +
'}';
}
@Override
public int compareTo(MyDate o) {// 把比较放在这里
int yearMinus=year- o.getYear();
// 继续判断
if (yearMinus!=0){
return yearMinus;
}
else {
int monthMinus=month-o.getMonth();
// 再判断
if (monthMinus!=0){
return monthMinus;
}
else {
return day-o.getDay();
}
}
}
}
其中,并调用sort 方法传入 Comparator 对象(使用泛型) 先按照name 排序,name相同则按照日期先后,关于Comparator的使用在我的另一篇文章(java 集合构架)中我有详细介绍.
4.泛型通配符使用:
package fanxing.com;
// 泛型继承和通配符
import java.util.ArrayList;
import java.util.List;
public class Generic_Extends {
public static void main(String[] args) {
Object o=new String("majuniy");// 没有问题
System.out.println(o);
// 泛型不具备继承性 以下使用错误
//List<Object> list=new ArrayList<String>();
/**
* 泛型通配符
*1. <?> :支持任意泛型类型
* 2. <? extends A> : 支持 A类以及A 类的子类,规定了泛型的上限
* 3. <? super A> : 支持 A类以及A 类的父类,不限于直接父类,规定了泛型的下限 包括 Object 类
*/
ArrayList<Object> list1 = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>();
ArrayList<X> list3 = new ArrayList<>();
ArrayList<Y> list4 = new ArrayList<>();
ArrayList<Z> list5 = new ArrayList<>();
// 如果是 <?> :支持任意泛型类型
printCollenction1(list1);
printCollenction1(list2);
printCollenction1(list3);
printCollenction1(list4);
printCollenction1(list5);
// 如果是 <? extends X> : 支持 X类以及X 类的子类,规定了泛型的上限
// printCollenction2(list2); 错误使用
printCollenction2(list3);
printCollenction2(list4);
printCollenction2(list5);
// 如果是 <? super Z> : 支持 Z类以及Z 类的父类,不限于直接父类,规定了泛型的下限 包括 Object 类
printCollenction3(list1);
printCollenction3(list3);
printCollenction3(list4);
printCollenction3(list5);
}
// 编写几个方法:
public static void printCollenction1(List<?> c){ // 支持任意泛型类型
for (Object o :c) {
System.out.println(o);
}
}
public static void printCollenction2(List<? extends X> c){// 支持 X类以及X 类的子类,规定了泛型的上限
for (Object o :c) {
System.out.println(o);
}
}
public static void printCollenction3(List<? super Z> c){
// 支持 Z类以及Z 类的父类,不限于直接父类,规定了泛型的下限 包括 Object 类
for (Object o :c) {
System.out.println(o);
}
}
}
class X{
}
class Y extends X{
}
class Z extends Y{
}
5.?JUnit 单元类测试框架:
关于java JUnit 测试类的使用我就为大家准备了IDEA操作截图 ? ? ??
那首先让我们先来了解一下什么是 JUnit 测试框架呢:
平时我们调用方法都是:
public class GenericTest05 {
public static void main(String[] args) {
test1();
}
public static void test1(){
System.out.println("调用成功!!!");
}
}
而且该方法必须是静态方法才能被调用成功。
那来看看有了Junit 时:
?
有了JUnit 我们可以单独调用一个方法,并且它可以是非静态方法。
这样就可以大大减少我们在测试时对 main方法的修改,下面为大家展示 如何生成JUnit 测试框架:
1. 首先在要测试的方法前面写上 @Test
2. 然后点击报错灯(当然你也可以快捷键进入)选择将" JUnit 5.70”添加到类路径中,因为我已经配置过过了,因此在这里就没有显示,当然JUnit 4也是可以的,但现在更推荐使用JUnit 5.7,跟上技术的更新嘛。
3. 然后点击确定,稍微等待一下他进行配置完成以后就可以了,配置一次就可以了,以后如果还需要使用的话,输入 @Test 回车即可生成?
?好了,通过以上对泛型的学习呢,让我们来看看一道关于泛型较为系统的题吧:
package fanxing.com;
import org.junit.jupiter.api.Test;
import java.util.*;
// 泛型练习以及 JUnit 测试框架
/**
* 要求:
* 定义泛型类DAD<T> ,在其中定义一个Map成员变量,Map键为String类型,值为T类型
* 分别创建以下方法:
*1. public void save(String id,T entity) 保存T类型对象到 Map 中
* 2. public T get(String id) 从Map 中取出id 对应的对象
*3. public void update(String id,T entity) 替换map 中key 对应的 Value
* 4. public List<T> list() 返回map 中的所有T对象并存入 List集合,ArrayList
* 5. public void delete(String id){ // 删除id 对应的对象
*
*定义一个 User类该类有:
* private int id;
* private int age;
* private String name;
*
* 创建DAD对象,分别调用save、get、 update、list、delete 方法操作User 对象
*使用JUnit 单元测试类进行测试
*/
public class GenericTest05 {
public static void main(String[] args) {
}
@Test
public void testList(){
DAD<User> dao = new DAD<>();
// 添加
dao.save("001",new User(2002,19,"tom"));
dao.save("002",new User(2001,20,"jact"));
dao.save("003",new User(2003,18,"john"));
List<User> list=dao.list();//输出看看T(User) 对象
System.out.println(list);
// 将"003" 改为
dao.update("003",new User(2000,21,"风煞"));
list=dao.list();
System.out.println(list);
// 删除 ”001“
dao.delete("001");
list=dao.list();
System.out.println(list);
// 单独获取"003"
System.out.println(dao.get("003"));
// 好了其他方法我就不一一演示了
}
}
class User{
private int id;
private int age;
private String name;
public User(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
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;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "\nUser{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
class DAD<T>{
private Map<String,T> map = new HashMap<>();
public void save(String id,T entity){// 保存T类型对象到 Map 中
map.put(id, entity);
}
public T get(String id){// 从Map 中取出id 对应的对象
return map.get(id);
}
public void update(String id,T entity){// 替换map 中key 对应的 Value
if(map.containsKey(id)){// 先判断是否存在该对象
map.put(id,entity);// 若存在添加即替换
}
else System.out.println("不存在需要替换的对象");
}
public List<T> list(){// 返回map 中的所有T对象并存入 List集合,ArrayList
// 遍历Map
List<T> list=new ArrayList<>();
Set<Map.Entry<String, T>> entries = map.entrySet();
Iterator<Map.Entry<String, T>> iterator = entries.iterator();
while (iterator.hasNext()) {
Map.Entry<String, T> next = iterator.next();
list.add(next.getValue());
}
return list;
}
public void delete(String id){ // 删除id 对应的对象
if(map.containsKey(id)){
map.remove(id);
}
else System.out.println("不存在需要删除的对象");
}
}
谢谢大家,有问题欢迎评论哦!!!
|