Подробное объяснение дженериков Java + умное использование исходного кода Ali fastjson

Java

введение

Прежде чем говорить об Али fastjson, давайте поговорим о некоторых базовых знаниях о дженериках и о том, как получить дженерики в отражении.Если вы чувствуете, что освоили это, вы можете перейти к последнему просмотру прямо через каталог.

общий класс

В определении универсального класса нужно только добавить непосредственно после имени класса при объявлении класса.E в нем может быть любой буквой, или их может быть более одной, и несколько могут быть разделены запятыми. Пример кода выглядит следующим образом

public class SelfList<E> {}

Вывод фактического типа в универсальном классе

Итак, когда определяется конкретный тип этого E? На самом деле, когда объект новый, см. следующий код

public class SelfList<E> {

    public void add(E e) {
        if (e instanceof String) {
            System.out.println(" I am String");
        } else if (e instanceof Integer) {
            System.out.println("I am Integer");
        }
    }
    public static void main(String[] args) {
        //这里创建的时候指定了String
        SelfList<String> a = new SelfList<String>();
        a.add("123");
        //这里创建的时候指定了Integer
        SelfList<Integer> b = new SelfList<Integer>();
        b.add(123);
    }

}

общий интерфейс

Общие интерфейсы используются так же, как и классы.

public interface IndicatorTypeService<T> {}

//这里定义的时候指定具体类型为ProductSale,当然也可以这里没有指定具体类型   
public class ProductSaleServiceImpl implements IndicatorTypeService<ProductSale> {}

общий метод

Я думаю, что это относительно редко, все обращают внимание и слушают меня, может быть, общий метод, который вы всегда считали подделкой. Что ж, позвольте мне дать вам поддельный общий метод для проверки.Это все еще пример приведенного выше кода.Для удобства чтения я снова вставлю код.

   //注意这是个假的泛型方法,不要以为有一个E就是泛型方法哦
    public void add(E e) {
        if (e instanceof String) {
            System.out.println(" I am String");
        } else if (e instanceof Integer) {
            System.out.println("I am Integer");
        }
    }

Определение универсального метода

Хорошо, настало время определить настоящий общий метод

    public <T> T get(T t) {
        return t;
    }
  1. между возвращаемым значением и общедоступным является необходимым условием для универсального метода, и это и универсальный E, определенный классом, могут иметь одно и то же имя (обычно задавать разные имена), и они независимы.
  2. может быть несколько, разделенных запятыми, например
  3. Возвращаемое значение не обязательно должно быть T, оно может быть любого типа, например Long
  4. Параметр в методе не обязательно должен быть T, он может быть любого типа, например Long. Просто универсальные методы обычно возвращают типы значений и параметры, один из которых является определенным универсальным типом (все конкретные типы бессмысленны).
    public <T> T get(T t) {
        return t;
    }
    public static void main(String[] args) {
        //这里创建的时候指定了String
        SelfList<String> a = new SelfList<String>();
        a.add("123");
        int num = a.get(123);
    }

Вывод фактического универсального типа в универсальном методе

Так как же общий метод определяет этот конкретный тип? Основная идея состоит в том, чтобы определить конкретный тип типа параметра и типа возвращаемого значения, переданного путем вызова универсального метода.

  1. Универсальные переменные появляются в списке параметров только один раз и определяются в соответствии с фактическими типами параметров, передаваемыми при вызове метода.
    public <T>  T get1(T t) {
        if (t instanceof String) {
            System.out.println(" I am String");
        } else if (t instanceof Integer) {
            System.out.println("I am Integer");
        }
        return t;
    }

    public static void main(String[] args) {
        SelfList<String> a = new SelfList<String>();
        //这里调用的时候传进去的是int 类型,所以确定了他的类型是int
        int b=a.get1(123);
    }

выходной результат

I am Integer
  1. Когда несколько параметров в списке параметров используют одну и ту же универсальную переменную, а тип возвращаемого значения также использует эту переменную, то тип возвращаемого значения определяется их общим родительским классом для определения окончательного универсального типа.
    public <T> T get2(T t, T t2) {
        if (t instanceof Float) {
            System.out.println(" I am String");
        } else if (t instanceof Integer) {
            System.out.println("I am Integer");
        } else if (t instanceof Number) {
            System.out.println("I am Number");

        }
        return t;
    }

    public static void main(String[] args) {
        SelfList<String> a = new SelfList<String>();
        //这里返回值类型必须是number 类型
        Number b = a.get2(123, 12.1);
    }

Обратите внимание, что вывод выше также будет

I am Integer

Потому что по первому правилу какой тип передается в какой тип, а тип возвращаемого значения нужно определять по второму правилу

Общее использование в отражении

Все вышеперечисленное является дженериками, которые можно определить перед компиляцией. Как мы все знаем, дженерики фактически стираются при запуске. Но это не беда, нам все же дано получить его через рефлексию. Во-первых, позвольте мне понять структуру наследования универсального типа в java.

Здесь мы в основном говорим о трех классах, которые чаще всего используются в обычное время, и есть некоторые другие GenericArrayTypes, которые не упомянуты, вы можете посмотреть на них в соответствии с моей идеей анализа.

Исходный код параметризованного типа

public interface ParameterizedType extends Type {
    /**
     * 返回一个实际类型的数组
     * 比如对于ArrayList<T>,被实例化的时候ArrayList<String>,这里返回的就是String
     *
     */
    Type[] getActualTypeArguments();

    /**
     * 这里其实就是返回去掉泛型后的真实类型
     * 对于List<T> 这里返回就是List
     */
    Type getRawType();

    /**
     * 这里针对的是内部类的情况,返回的是他的外层的类的类型
     * 例如SelfHashMap  里面有一个Entry 内部类,也就是SelfHashMap.Entry<String,String>
     * 返回的就是SelfHashMap
     */
    Type getOwnerType();
}

Проверка проверки ParameterizedType

определить класс с внутренним классом

public class SelfHashMap {

    public static class Entry<T, V extends Collection> {

    }

}

написать тестовый класс

public class ParameterizedTypeTest {

    public static void main(String[] args) {
        Class clazz = A.class;
        //注意这里是拿父类的泛型,jdk 没有提供本类的泛型
        Type type = clazz.getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            System.out.println(Arrays.toString(parameterizedType.getActualTypeArguments()));
            System.out.println(parameterizedType.getRawType());
            System.out.println(parameterizedType.getOwnerType());
        }




    }

    /**
     * 这里的泛型被是指定了为String 和List
     * getActualTypeArguments() 会输出 String 和List
     */
    public static class A extends SelfHashMap.Entry<String, List> {

    }

}

выходной результат

[class java.lang.String, interface java.util.List]
class com.wizhuo.jdklearning.reflect.dto.SelfHashMap$Entry
class com.wizhuo.jdklearning.reflect.dto.SelfHashMap

Исходный код TypeVariable

public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {
    /**
     * 这里返回的是泛型的上界类型数组,比如 <T, V extends Collection> 这里的上界类型就是Collection
    */
    Type[] getBounds();

    /**
     * 返回的是声明该泛型变量的类,接口,方法等
     * 列如 public static class Entry<T, V extends Collection> 返回的就是Entry
     */
    D getGenericDeclaration();

    /**
     * 这里返回的就是泛型定义的变量名称,比如 <T>  返回的就是T
     *
     */
    String getName();

    /**
     * 这里返回的就是AnnotatedType 数组,jdk1.8 才加进来,本文不分析,直接跳过
     */
     AnnotatedType[] getAnnotatedBounds();
}

написать тестовый класс

public class TypeVariableTest {

    public static void main(String[] args) {
        Class clazz = SelfHashMap.Entry.class;
        TypeVariable[] typeVariables = clazz.getTypeParameters();
        for (TypeVariable ty :typeVariables) {
            System.out.println(ty.getName());
            System.out.println(Arrays.toString(ty.getAnnotatedBounds()));
            System.out.println((Arrays.toString(ty.getBounds())));
            System.out.println(ty.getGenericDeclaration());
            System.out.println("============================");
        }

    }

}

выходной результат

T
[sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl@3fee733d]
[class java.lang.Object]
class com.wizhuo.jdklearning.reflect.dto.SelfHashMap$Entry
============================
V
[sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl@5acf9800]
[interface java.util.Collection]
class com.wizhuo.jdklearning.reflect.dto.SelfHashMap$Entry

Умное использование дженериков Ali fastjson

Глядя на следующий код, простой смысл состоит в том, чтобы получить строку из Redis и преобразовать ее в указанный общий класс. Вы можете заметить, что здесь создается анонимный класс TypeReference. Обратите внимание, что позади находится {}, поэтому создается экземпляр анонимного внутреннего класса (это ключевой момент, здесь начинается вся магия), а не экземпляр класса TypeReference.

        List<ResourceEntity> resources =
            redisAdapter.get(BaseConstant.TENANT_CODE_SYSTEM, CacheKey.KEY_ALL_RESOURCE,
                new TypeReference<List<ResourceEntity>>() {
                });

Шпионить за исходным кодом

Нажмите здесь, чтобы увидеть исходный код


    protected final Type type;
    /**
     *注意这里protected ,也就意味着我们创建的时候只能继承这个类成为他的子类,毕竟我们的类
     * 不可能和阿里巴巴的fastjson 在同一个包目录下
     *
     * 一般我们都是创建一个匿名内部类来成为他的子类,然后泛型中传进我们想要转化的最终泛型 
     * 例如上面的代码new TypeReference<List<ResourceEntity>>() {}  List<ResourceEntity> 是我们想要转化的类型
     *           
     * 
     */
    protected TypeReference(){
        //这里是获取父类的泛型,由于jdk 不支持获取自己的泛型,这里巧妙的通过继承这个类,变成获取子类的泛型来解决
        Type superClass = getClass().getGenericSuperclass();
        //这里返回泛型的实际类型,就是 List<ResourceEntity>
        type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

прочитать две вещи

Если вы считаете, что этот контент вас очень вдохновляет, я хотел бы пригласить вас сделать мне 2 небольших одолжения:

  1. Ставьте лайк, чтобы больше людей увидело этот контент
  2. Обратите внимание на паблик "Летучая мышь интервью", делитесь оригинальными знаниями время от времени, оригинальность не простая, поддержите пожалуйста побольше (там же есть небольшая программа для расчесывания вопросов).