предисловие
Когда я впервые связался с Java, я был скомпилирован, и я бегал везде, я чувствую себя очень сильным, я должен войти в яму. Но сейчас мейнстрим Java использует сцены или бэкэнд, и разработка графического интерфейса не очень хороша. Java по-прежнему остается в конкурентной борьбе среди серверных языков, и экология Java имеет прекрасные отношения, но нельзя отрицать, что Java действительно надежен в сценах с большим числом одновременных вычислений. Я также прочитал оценку языка голаанг, а также готов рассмотреть поступление в качестве технического резерва. Когда у меня есть время учиться, я должен учиться больше.
В последнее время я планирую уделить время пересмотру java, и я решил взглянуть на исходный код jdk и поднять его на новый уровень. Также хочу написать серию блогов о java и SpringBoot.
Дженерики
До JDK 1.5
Когда я попал в яму java, это был еще мир jdk 1.7. Однако дженерики — это хорошо. Перед дженериками мы могли бы написать такой код
@Test
public void run1() {
// 有个集合你想保存 String 类型数据
List list =new ArrayList();
list.add("1111");
String o = (String) list.get(0);
System.out.println(o.length());
// 可能会导致别的开发人员存储的是 int 类型
list.add(2222);
}
Проблемы с приведенным выше кодом:
- Элементы, хранящиеся в коллекциях, должны быть преобразованы для использования соответствующих API.
- Синтаксис времени компиляции не может обнаружить проблему, которую я упомянул, и во время выполнения будут скрытые опасности.
JDk 1.5
1.7 Добавлены функции Алмазные дженерики, которые я смешал вместе, чтобы сказать
// 有个集合你想保存 String 类型数据
List<String> list =new ArrayList();
list.add("1111");
String s = list.get(0);
System.out.println(s.length());
// 下列会在编译时期就会报错
// list.add(2222);
Разве это не весело и не так античеловечно?
Дженерики
Общие сведения: ввод параметров
- Обобщения могут быть объявлены в классах или методах, а обобщения в методах имеют приоритет над классами. Для простоты понимания лучше не использовать то же имя, что и объявление класса.
- Статические методы не могут быть объявлены с использованием дженериков в классе, только в методах.
- Часто используемые общие объявления (T, E, K, V,?)
- ? представляет неопределенный тип java
- T (тип) представляет определенный тип Java
- K V (значение ключа) соответственно представляют значение ключа в значении ключа java.
- E (элемент) означает элемент
- S, U, V и т. д.: типы 2, 3, 4 в многопараметрическом случае.
проверка кода
- Обобщения могут быть объявлены в классах или методах, а обобщения в методах имеют приоритет над классами.
public class ClientTest<T> {
/**
* 验证泛型可以在类上和方法上声明
* 不能这样 <K1 super Parent>
*/
public <T2 extends ArgsParent> T2 run2(T k) {
System.out.println(k.getName());
System.out.println(k);
return (T2) k;
}
}
Вышеупомянутое подтверждает, что дженерики могут быть объявлены в классах и методах.
package com.fly.study.java.generics;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
/**
* @author 张攀钦
* @date 2019-09-16-00:04
* @description 泛型验证
*/
public class ClientTest<T> {
private ClientTest clientTest;
private Parent parent;
private Son son;
private ClientTest<Son> sonClientTest;
private ArgsParent argsParent;
private ArgsSon argsSon;
/**
* 限制方法参数的泛型和返回值类型,泛型优先使用方法上的,当方法上没有使用类上的
* 不能这样 <K1 super Parent>
*/
public <T extends ArgsParent, T2 extends ArgsParent> T2 run2(T k) {
System.out.println(k.getName());
System.out.println(k);
return (T2) k;
}
@Before
public void before() {
clientTest = new ClientTest();
parent = new Parent();
parent.setName("parent");
son = new Son();
son.setAge(18);
sonClientTest = new ClientTest<>();
argsParent = new ArgsParent();
argsParent.setName("argsParent");
argsSon = new ArgsSon();
}
@Test
public void test2() {
System.out.println(clientTest.run2(argsParent));
System.out.println(clientTest.run2(argsSon));
// 验证方法上的泛型优先于类上的泛型
sonClientTest.run2(argsSon);
// 下面用法错误
// sonClientTest.run2(son);
}
}
@Data
public class ArgsParent {
private String name;
}
@Data
public class ArgsSon extends ArgsParent {
private Integer age;
}
@Data
public class Parent {
private String name;
}
@Data
public class Son extends Parent {
private Integer age;
}
Я использую один и тот же общий T в классе и методе, но ограничения типа разные, универсальный T в классе — это ClientTest sonClientTest, когда я создаю новый объект, и я указываю T в методе, как при запуске метода, если параметры может передаваться только в ArgsParent и его подклассах, я могу проверить свой вывод.
// public class ClientTest<T>
// public <T extends ArgsParent, T2 extends ArgsParent> T2 run2(T k)
// argsSon 为 ArgsParent 的子类对象
sonClientTest.run2(argsSon);
// 下面用法错误,son 为 Son 的实例对象
// sonClientTest.run2(son);
- Статические методы не могут использовать общие объявления в классах, они могут быть объявлены только в методах.
public class ClientTest<T> {
/**
* 静态方法上的泛型不能使用类上的,只能再方法上声明泛型
*/
public static <T extends ArgsParent, T2 extends ArgsParent> void run4(T k) {
System.out.println(k);
}
// 语法错误
public static void run4(T k) {
System.out.println(k);
}
}
- общий квалификатор
Родовой квалификатор очень удобно понимать буквально, а комментарии в коде могут пояснить смысл
// 限定参数类型只能为 ArgsParent及其子类
<S extends ArgsParent>
// 语法错误,jdk 1.8
<S super ArgsParent>
// 限定参数类型只能为 ArgsParent 及其子类
<? extends ArgsParent>
// 限定参数只能为 ArgsParent 及其父类
<? extends ArgsParent>
public class AllGenerics<T> {
public void run1(T t) {
System.out.println(t);
}
public <S> void run2(S s) {
System.out.println(s);
}
// 限定参数类型只能为 ArgsParent及其子类
public <S extends ArgsParent> void run3(S s) {
System.out.println(s);
}
// 语法错误,
// public <S super ArgsParent> void run3(S s) {
// System.out.println(s);
//}
// 限定参数类型只能为 ArgsParent及其子类
public void run4(List<? extends ArgsParent> s) {
System.out.println(s);
}
// 限定参数只能为 ArgsParent及其父类
public void run5(List<? super ArgsParent> s) {
System.out.println(s);
}
}
Общий стирание
Общий эффективен только во время компиляции, будет заменен после составления общего оператора, чтобы определить тип неопределенности вместо того, чтобы использовать объект, чтобы определить тип использования.
- Когда универсальный тип не может быть определен
// 源码
public class AllGenerics<T> {
public void run1(T t) {
System.out.println(t);
}
}
// 编译之后的代码可以这样理解
public class AllGenerics<Object> {
public void run1(Object t) {
System.out.println(t);
}
}
- Используйте общие квалификаторы для
public class AllGenerics<T> {
public <S extends ArgsParent> void run2(S s) {
System.out.println(s);
}
}
// 编译之后的代码可以这样理解
public class AllGenerics<ArgsParent> {
public void run1(ArgsParent t) {
System.out.println(t);
}
}
проверка кода
- Дженерики действительны только во время компиляции
@Data
public class ClientTestSuperT {
@Test
public void test1() throws Exception{
List<String>t=new ArrayList<>();
Method add = t.getClass().getMethod("add",Object.class);
// 添加 Integer 类型数字
add.invoke(t,1);
System.out.println(t);
}
}
- Общее стирание использует замену определяемого типа
package com.fly.study.java.generics;
import org.junit.Test;
import java.lang.reflect.Method;
public class AllGenerics<T> {
public <S extends ArgsParent> void run2(S s) {
System.out.println(s);
}
public void run1(T t) {
System.out.println(t);
}
@Test
public void test1() throws Exception {
AllGenerics<String> allGenerics =new AllGenerics<>();
Method method= allGenerics.getClass().getMethod("run1", Object.class);
// java.lang.NoSuchMethodException:
// com.fly.study.java.generics.AllGenerics.run1(java.lang.String)
// Method method= allGenerics.getClass().getMethod("run1", String.class);
method.invoke(allGenerics,"111");
}
@Test
public void run2() throws Exception {
AllGenerics<String> allGenerics =new AllGenerics<>();
Method method= allGenerics.getClass().getMethod("run2", ArgsParent.class);
ArgsParent argsParent = new ArgsParent();
argsParent.setName("测试");
// java.lang.NoSuchMethodException:
// com.fly.study.java.generics.AllGenerics.run1(java.lang.String)
// Method method= allGenerics.getClass().getMethod("run2", Object.class);
method.invoke(allGenerics,argsParent);
}
}
- Идеи для проверки
AllGenerics<T> 编译之后 AllGenerics<Object>
// 编译之后为 public void run1(Object t)
public void run1(T t) {
System.out.println(t);
}
// 编译之后 public void run2(ArgsParent s)
public <S extends ArgsParent> void run2(S s) {
System.out.println(s);
}
- Используя Reflected Get Method run1, можно получить параметры объекта метода, получить runl, не удается найти параметр String
@Test
public void test1() throws Exception {
AllGenerics<String> allGenerics =new AllGenerics<>();
Method method= allGenerics.getClass().getMethod("run1", Object.class);
method.invoke(allGenerics,"111");
}
Приведенный выше код может работать нормально, убедитесь, что код действительно заменяется после компиляции.
@Test
public void test1() throws Exception {
AllGenerics<String> allGenerics =new AllGenerics<>();
Method method= allGenerics.getClass().getMethod("run1", String.class);
method.invoke(allGenerics,"111");
}
В приведенном выше коде возникает ошибка java.lang.NoSuchMethodException: com.fly.study.java.generics.AllGenerics.run1(java.lang.String), что указывает на то, что этот метод не существует в скомпилированном коде.
- Для дженериков, использующих квалифицированный тип, замените его квалифицированным типом.
// 方法编译之后为:public void run2(ArgsParent s)
public <S extends ArgsParent> void run2(S s) {
System.out.println(s);
}
@Test
public void run2() throws Exception {
AllGenerics<String> allGenerics =new AllGenerics<>();
Method method= allGenerics.getClass().getMethod("run2", ArgsParent.class);
ArgsParent argsParent = new ArgsParent();
argsParent.setName("测试");
method.invoke(allGenerics,argsParent);
}
Вышеупомянутый метод был получен, что указывает на то, что моя догадка верна
@Test
public void run2() throws Exception {
AllGenerics<String> allGenerics =new AllGenerics<>();
ArgsParent argsParent = new ArgsParent();
argsParent.setName("测试");
Method method= allGenerics.getClass().getMethod("run2", Object.class);
method.invoke(allGenerics,argsParent);
}
Код сообщает об ошибке, и соответствующий метод не может быть найден