1.方法泛型的结构
泛型声明:在方法签名的前面,使用尖括号<>来声明泛型类型。
例如
public static <T> void printElements(List<T> list) {}
1.1.方法泛型的机制和作用
生命周期
泛型在java中主要存在于两个生命周期
- 编译阶段:Java编译器会读取泛型代码,并将类型参数替换为具体的类型。例如,如果编译器推断
T应该是String类型,那么编译后的代码会使用String类型替换泛型。 - 字节码生成阶段:在字节码生成阶段,Java编译器会将泛型代码转换成字节码。由于泛型擦除,这个阶段会将泛型信息从字节码中移除,只保留原始的类型信息。
泛型擦除(Generics Erasure)是Java泛型机制的一个特性,它指的是在Java编译器将泛型代码编译成字节码时,泛型信息会被从代码中移除。
作用
在使用时,我们只需要关注调用方法时,泛型被替换的具体类型.
通过这种方式,在保证传递参数规范的前提下,可以提供更高的方法灵活性和可复用性
以下来自ai
方法泛型在Java编程语言中具有以下作用:
- 类型安全:泛型方法在编译时执行类型检查,确保方法操作的参数和返回值类型是正确的。这有助于避免在运行时由于类型不匹配导致的错误。
- 代码复用:泛型方法可以接受多种类型的参数,从而避免了为每种类型编写重复的代码。这意味着你可以编写一个通用的方法来处理多种类型的数据,提高了代码的复用性。
- 明确意图:泛型方法的使用可以清楚地表明其意图,即该方法可以处理多种类型的对象。这有助于提高代码的可读性和可维护性。
- 灵活性:泛型方法允许你在不知道具体类型的情况下编写代码,这使得代码更加灵活和通用。
- 避免强制类型转换:泛型方法通常不需要进行强制类型转换,因为编译器已经确保了类型的匹配。这有助于减少代码中的类型转换错误。
- 类型推断:在某些情况下,编译器可以自动推断出泛型类型,这使得代码更加简洁。
总之,泛型方法在Java编程中是一种强大的工具,它提供了一种类型安全的、可重用的、灵活的方式来编写处理多种类型数据的代码。
1.2.举例
和class,list本身带有泛型的类使用
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("Hello");
stringList.add("World");
List<Integer> integerList = new ArrayList<>();
integerList.add(1);
integerList.add(2);
String s = printElements(stringList, "nh", String.class);// 调用时提供String类型的参数
Integer i = printElements(integerList, 123L,Long.class);// 调用时提供Integer类型的参数
}
public static <U,KK> U printElements(List<U> list,KK kk,Class<KK> demo) {
for (U element : list) {
System.out.println(element);
}{
return (U) kk;
}
这段代码中,第二次调用U为Integer,Class为Long,同时demo为Long.class 当使用这个方法时,参数之间会检查 会发生编译器报错 当我们用这种方法调用
Integer i = printElements(integerList, 123,Long.class);
1.3.举例
实现列表的转换
public class BeanUtil {
public static <T,V> List<V> mad(List<T>source,Class<V> target) {
List<V> collect = source.stream()
.map(e -> {
try {
V v = cn.hutool.core.bean.BeanUtil.copyProperties(e, target);
return v;
} catch (Exception ex) {
// 处理异常,例如:打印日志或抛出自定义异常
throw new RuntimeException(ex);
}
})
.collect(Collectors.toList());
return collect;
}
}
2.反射
反射把类抽象,可以调用类,构造方法,方法,成员变量,修饰符,注解等等
基础概念
常见的:构造方法,方法,成员变量的调用方式:通过类获取构造器 构造器参数:传入对象实例,传入可能参数
详细
获取类的三种方法 1.Class<?> cls = Class.forName("java.lang.String");
2.String str = "Hello, World!"; Class<?> cls = str.getClass();
3.对象实例 x.class
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
class Student {
String name;
int age;
public void method(String msg){
System.out.println(msg);
System.out.println(this.getName());
}
}
结合实践分析反射使用
package com.example.wing.client.minio;
import com.example.wing.project.commom.service.FileService;
import com.example.wing.project.sys.domain.User;
import liquibase.pro.packaged.T;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.List;
import static com.alibaba.fastjson2.util.BeanUtils.getterName;
import static com.alibaba.fastjson2.util.BeanUtils.setterName;
/**
* <h3>wing</h3>
* <p>泛型方法</p>
*
* @author : zlx
* @date : 2024-07-19 11:12
**/
@Component
public class UrlUtil {
@Autowired
private FileService fileUploadService;
public <T> void updateMinioUrl(List<T> items, String urlFieldName) {
if (items != null) {
items.forEach(item -> {
String minioUrl = null;
try {
minioUrl = (String) item.getClass().getMethod(
//return "get" + first.toUpperCase() + last;
getterName(urlFieldName)).invoke(item);
} catch (Exception e) {
e.printStackTrace();
}
if (StringUtils.isNotBlank(minioUrl)) {
try {
String presignedUrl = fileUploadService.getPreSign(minioUrl);
//
item.getClass().getMethod(setterName(urlFieldName), String.class).invoke(item, presignedUrl);
} catch (Exception e) {
e.printStackTrace();
}
}
})
;}
}
private String getterName(String name) {
String first = name.substring(0, 1);
String last = name.substring(1);
return "get" + first.toUpperCase() + last;
}
/**
* 转化set方法名
*/
private String setterName(String name) {
String first = name.substring(0, 1);
String last = name.substring(1);
return "set" + first.toUpperCase() + last;
}
}
这是一个工具类,作用是把指定对象的field转化
- 自定义构建get和set的方法,可以访问private成员变量
- 通过反射操作实例,实现一定的灵活性,实例的地址是唯一的,因此可以动态修改实例的数值
- 结合之前的限流文章,aop中获得的args参数也都是可以直接修改
Student student = new Student();
student.setAge(18);
student.setName("小明");
long l = VM.current().addressOf(student);
System.out.println(l);
Field name = student.getClass().getDeclaredField("name");
name.setAccessible(true);
System.out.println(name.get(student));
name.set(student,"小红");
long l1 = VM.current().addressOf(student);
System.out.println(l1);
System.out.println(l==l1);
System.out.println(student);
3.常见的labda对象
Function<T, R>
抽取方法
-
用途:接受一个类型为
T的参数,并返回一个类型为R的结果。 -
调用方法:
apply(T t)- 用于执行函数操作并返回结果。Function<String, Integer> length = s -> s.length(); Integer len = length.apply("Hello");空function,传入什么,传出什么
Function.identity()
Consumer
-
用途:接受一个类型为
T的参数,无返回值。用于执行一个操作。 -
调用方法:
accept(T t)- 用于执行操作。Consumer<String> print = s -> System.out.println(s); print.accept("Hello");
Supplier
aop中joint的传递
-
用途:不接受参数,但提供一个
T类型的结果。用于生成一个值。 -
调用方法:
get()- 用于获取结果。Supplier<String> helloSupplier = () -> "Hello"; String hello = helloSupplier.get();
BiFunction<T, U, R>
CF的链式调用
-
用途:接受两个参数,分别为
T和U类型,返回一个R类型的结果。 -
调用方法:
apply(T t, U u)- 用于执行函数操作并返回结果。BiFunction<String, String, String> concatenate = (a, b) -> a + b; String result = concatenate.apply("Hello", "World");