目录
3. 自定义泛型结构
3.1 泛型的基础说明
3.2 自定义泛型类或泛型接口
3.2.1 说明
3.2.2 注意
3.2.2 举例
3.2.3 练习
3.3 自定义泛型方法
3.3.1 说明
3.3.2 举例
3.3.3 练习
1、<类型>这种语法形式就叫泛型。
<类型>的形式我们称为类型参数,这里的"类型"习惯上使用T表示,是Type的缩写。即:
类比方法的参数的概念,我们把
这里的T,可以替换成K,V等任意字母。
2、在哪里可以声明类型变量
声明类或接口时,在类名或接口名后面声明泛型类型,我们把这样的类或接口称为泛型类
或泛型接口
。
【修饰符】 class 类名<类型变量列表> 【extends 父类】 【implements 接口们】{
}
【修饰符】 interface 接口名<类型变量列表> 【implements 接口们】{
}//例如:
public class ArrayList
public interface Map{
....
}
[修饰符] <类型变量列表> 返回值类型 方法名([形参列表])[throws 异常列表]{
//...
}//例如:java.util.Arrays类中的
public staticList asList(T... a){
....
}
当我们在类或接口中定义某个成员时,该成员的相关类型是不确定的,而这个类型需要在使用这个类或接口时才可以确定,那么我们可以使用泛型类、泛型接口。
① 我们在声明完自定义泛型类以后,可以在类的内部(比如:属性、方法、构造器中)使用类的泛型。
② 我们在创建自定义泛型类的对象时,可以指明泛型参数类型。一旦指明,内部凡是使用类的泛型参数的位置,都具体化为指定的类的泛型类型。
③ 如果在创建自定义泛型类的对象时,没有指明泛型参数类型,那么泛型将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。
经验:泛型要使用一路都用。要不用,一路都不要用。
④ 泛型的指定中必须使用引用数据类型。不能使用基本数据类型,此时只能使用包装类替换。
⑤ 除创建泛型类对象外,子类继承泛型类时、实现类实现泛型接口时,也可以确定泛型结构中的泛型参数。
如果我们在给泛型类提供子类时,子类也不确定泛型的类型,则可以继续使用泛型参数。
我们还可以在现有的父类的泛型参数的基础上,新增泛型参数。
① 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:
② JDK7.0 开始,泛型的简化操作:ArrayList
③ 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
④ 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity];
参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。
⑤ 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,但不可以在静态方法中使用类的泛型。
⑥ 异常类不能是带泛型的。
举例1:
class Person {
// 使用T类型定义变量
private T info;
// 使用T类型定义一般方法
public T getInfo() {
return info;
}
public void setInfo(T info) {
this.info = info;
}
// 使用T类型定义构造器
public Person() {
}
public Person(T info) {
this.info = info;
}
// static的方法中不能声明泛型
//public static void show(T t) {
//
//}
// 不能在try-catch中使用泛型定义
//public void test() {
//try {
//
//} catch (MyException ex) {
//
//}
//}
}
举例2:
class Father {
}
// 子类不保留父类的泛型
// 1)没有类型 擦除
class Son1 extends Father {// 等价于class Son extends Father
举例3:
class Father {
}
// 子类不保留父类的泛型
// 1)没有类型 擦除
class Son extends Father{//等价于class Son extends Father
定义个泛型类 DAO
,在其中定义一个Map 成员变量,Map 的键为 String 类型,值为 T 类型。 分别创建以下方法:
public void save(String id,T entity): 保存 T 类型的对象到 Map 成员变量中
public T get(String id):从 map 中获取 id 对应的对象
public void update(String id,T entity):替换 map 中key为id的内容,改为 entity 对象
public Listlist():返回 map 中存放的所有 T 对象
public void delete(String id):删除指定 id 对象定义一个 User 类:
该类包含:private成员变量(int类型) id,age;(String 类型)name。定义一个测试类:
创建 DAO 类的对象, 分别调用其 save、get、update、list、delete 方法来操作 User 对象,
使用 Junit 单元测试类进行测试。
DAO.java
package exer2;
import java.util.*;
/**
* ClassName:IntelliJ IDEA
* Description:
* 定义个泛型类 DAO,在其中定义一个Map 成员变量,Map 的键为 String 类型,值为 T 类型。
*
* 分别创建以下方法:
* public void save(String id,T entity): 保存 T 类型的对象到 Map 成员变量中
* public T get(String id):从 map 中获取 id 对应的对象
* public void update(String id,T entity):替换 map 中key为id的内容,改为 entity 对象
* public List list():返回 map 中存放的所有 T 对象
* public void delete(String id):删除指定 id 对象
*
* @Author zyjstart
* @Create:2024/10/5 22:48
*/
public class DAO {
Map map ;
{
map = new HashMap<>();
}
public void save(String id,T entity){ //保存 T 类型的对象到 Map 成员变量中
if (!map.containsKey(id)){ // 不包含该id的时候进行添加,如果添加前包含该id,则变成了替换了
map.put(id,entity);
}
}
public T get(String id){ // 从 map 中获取 id 对应的对象
return map.get(id);
}
public void update(String id,T entity){ // 替换 map 中key为id的内容,改为 entity 对象
if (map.containsKey(id)){ // 包含该key时,进行替换
map.put(id,entity);
}
}
public List list(){ // 返回 map 中存放的所有 T 对象
Collection values = map.values();
ArrayList list = new ArrayList<>();
list.addAll(values);
return list;
}
public void delete(String id){ // 删除指定 id 对象
map.remove(id);
}
}
User.java
package exer2;
import java.util.Objects;
/**
* ClassName:IntelliJ IDEA
* Description:
* 定义一个 User 类:
* 该类包含:private成员变量(int类型) id,age;(String 类型)name。
* @Author zyjstart
* @Create:2024/10/5 23:10
*/
public class User {
private int id;
private int age;
private String name;
public User() {
}
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 "User{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return id == user.id &&
age == user.age &&
Objects.equals(name, user.name);
}
@Override
public int hashCode() {
return Objects.hash(id, age, name);
}
}
DAOTest.java
package exer2;
import java.util.List;
/**
* ClassName:IntelliJ IDEA
* Description:
* 定义一个测试类:
* 创建 DAO 类的对象, 分别调用其 save、get、update、list、delete 方法来操作 User 对象,
* 使用 Junit 单元测试类进行测试。
* @Author zyjstart
* @Create:2024/10/5 23:11
*/
public class DAOTest {
public static void main(String[] args) {
DAO dao = new DAO<>();
//添加
dao.save("1001",new User(1,23,"流的话"));
dao.save("1002",new User(2,23,"力道山"));
//修改
dao.update("1002",new User(3,45,"陈亚玲"));
// 删除
dao.delete("1002");
List list = dao.list();
for (User user : list) {
System.out.println(user);
}
}
}
如果我们定义类、接口时没有使用<泛型参数>,但是某个方法形参类型不确定时,这个方法可以单独定义<泛型参数>。
泛型方法的格式:
[访问权限] <泛型> 返回值类型 方法名([泛型标识 参数名称]) [抛出的异常]{ }
方法,也可以被泛型化,与其所在的类是否是泛型类没有关系。
泛型方法中的泛型参数在方法被调用时确定。
泛型方法可以根据需要,声明为static的。
举例1:
public class DAO {
public E get(int id, E e) {
E result = null;
return result;
}
}
举例2:
public static void fromArrayToCollection(T[] a, Collection c) {
for (T o : a) {
c.add(o);
}
}
public static void main(String[] args) {
Object[] ao = new Object[100];
Collection
练习1: 泛型方法
编写一个泛型方法,实现任意引用类型数组指定位置元素交换。
public static
public class Exer01 {
//编写一个泛型方法,实现任意引用类型数组指定位置元素交换。
public static void method( E[] arr,int a,int b){
E temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
@Test
public void testMethod(){
Integer[] arr = new Integer[]{10,20,30,40};
method(arr,2,3);
for(Integer i : arr){
System.out.println(i);
}
}
}