泛型的引入, java允许设计者详细的描述变量和方法的类型要如何变化

定义简单泛型类

public class pair<T>
{
    private T first;
    private T second;
    
    public Pair() {first = null; second = null;}
    public Pair(T first, T second) {this.first = first, this.second = second;}
    
    public T getFirst() {return first;}
    public T getSecond() {return second;}
    
    public void setFirst(T newValue) {first = newValue;}
    public void setSecond(T newValue) {second = newValue;}
}

java库使用E表示集合的元素类型, K和V分别表示键和值的类型, T表示任意类型

pair1/PairTest1.java

package pair1;

/**
 * @author Cay Horstann
 */

public class PairTest1
{
    public static void main(String[] args)
    {
        String[] words = {"Mary", "had", "a", "little", "lamb"};
        Pair<String> mm = ArrayAlg.minmax(words);
        System.out.println("min = " + mm.getFirst());
        System.out.println("max = " + mm.getSecond());
    }
}

class ArrayAlg
{
    /**
     * Get the minimum and maximum of an array of strings,
     * @param a an array of strings
     * @return a pair with the min and max values, or null if a is null or empty
     */
    public static Pair<String> minmax(String [] a)
    {
        if (a == null || a.length == 0) return null;
        String min = a[0];
        String max = a[0];
        for (int i = 1; i < a.length; i++)
        {
            if(min.compareTo(a[i]) > 0) min = a[i];
            if(max.compareTo(a[i]) < 0) max = a[i];
        }
        return new Pair<>(min, max);
    }
 }

泛型方法

还可以定义一个带有类型参数的方法

class ArrayAlg
{
    public static <T> T getMiddle(T...a)
    {
        return a[a.length / 2];
    }
}

泛型方法可以在普通类内进行定义, 也可以在泛型类中

String middle = ArrayAlg.<String> getMiddle("John", "Q.", "Public");

大多数情况下可以省略类型参数

String middle = ArrayAlg.getMiddle("John", "Q.", "Public");

偶尔编译器也会提示错误, 此时需要自行解读

类型变量的限定

class ArrayAlg
{
    public static <T> min (T[] a) almost correct
    {
        if (a == null || a.length == 0) return null;
        T smallest = a[0];
        for (int i = 1; i < a.length; i++)
        {
            if(smallest.compareTo(a[i]) > 0) smallest = a[i];
        }
        return  smallest;
    }
}

T所属的类可能没有compareTo方法

解决方法是限制T只能是实现了Comparable接口, 可以通过对T设置限定(bound)来实现

public static <T extends Comparable> T min (T[] a)...

对于记法

T extends Comparable & Serializable

表示T应该是限定类型(bounding type)的子类型(subtype), T和限定类型可以是类, 也可以是接口, 其更接近子类型的概念

pair2/ PairTest2.java

package pair2;
import java.time.*;

/**
 * @author Cay Horstmann
 */

public class PairTest2 
{
    public static void main(String[] args)
    {
        LocalDate[] birthdays = 
        {
            LocalDate.of(1906, 12, 9) ,     //G. Hopper
            LocalDate.of(1815, 12, 10),     //A. Lovelace
            LocalDate.of(1903, 12, 3),      //J.von Neumann
            LocalDate.of(1910, 6, 22),      //K. Zuse
        };
        Pair<LocalDate> mm = ArrayAlg.minmax(birthdays);
        System.out.println("min = " + mm.getFirst());
        System.out.println("max = " + mm.getSecond());
    }
    
}

class ArrayAlg
{
    /**
     * Gets the minimum and maximum of an array of objects of type T.
     * @param a an array of objects of type T
     * @return a pair with the min and max values, of null if a is null or empty
     */

     public static <T extends Comparable> Pair<T> minmax(T[] a)
     {
         if (a == null || a.length == 0) return null;
         T min = a[0];
         T max = a[0];
         for (int i = 0; i < a.length; i++)
         {
            if (min.compareTo(a[i]) > 0) min = a[i];
            if (max.compareTo(a[i]) < 0) max = a[i];    
        }
        return new Pair<>(min, max);
     }
}

泛型代码和虚拟机

类型擦除

无论何时定义一个泛型类型, 都会自动提供一个相应的原始类型(raw type)。 这个原始类型的名字就是去掉类型参数后的泛型类型名。 类型变量会被擦除(erased), 并替换为其限定类型, 对于无限定类型的变量则替换为Object。

假定有如下声明:

public class Interval<T extends Comparable & Serializable> implements Serializable
{
    private T lower;
    private T upper;
    ...
    public Interval(T first, T second)
    {
        if(first.compareTo(second) <= 0) {lower = first; upper = second;}
        else
        {
            lower = second;
            upper =first;
        }
    }
}

原始类型如下:

public class Interval implements Serializable
{
    private Comparable lower;
    private Comparable upper;
    ...
    public Interval(Comparable first, Comparable second) {...}
}

转换泛型表达式

Pair<Employee> buddies = ...;
Employee buddy = duddies.geFirst();

编译器将getFirst方法拆分为两条虚拟机指令

  1. 对原始方法Pair.getFirst方法调用
  2. 将返回的Object类型强制转换为Employee类型

转换泛型方法

对于方法

public static <T extends Comparable>T min(T[] a)

擦除类型后

public static Comparable min (Comparable[] a)

对于

class DateInterval extends Pair<LocalDate>
{
    public void setSecond(LocalDate second)
    {
        if(second.compareTo(getFirst()) >= 0)
            super.setSecond(second);
    }
    ...
}

类型擦除后

class DateInterval extends Pair
{
    public void setSecond(LocalDate second){...}
}

还有一个从Pair继承的setSecond方法

public void setSecond(Object second)

对于下列语句

var interval = new DateInterval(...);
Pair<LocalDate> pair = interval;
pair.setSecond(aDate);

setSecond类型擦除和多态发生了冲突, 为解决该问题, 编译器在DateInterval中生成了一个桥方法(bridge method):

public void setSecond(Object second) {setSecond((LocalDate) second);}

对于java泛型的转换

  1. 虚拟机中没有泛型, 只有普通的类和方法
  2. 所有的类型参数都会替换为它们的限定类型
  3. 会合成桥方法来保持多态
  4. 为保持类型安全性, 必要时插入强制类型转换

调用历史遗留代码

java泛型的主要目的是允许泛型代码和恶遗留代码之间能够互操作

Dictionary<Integer, Component> labelTable = new HashTable<>();
labelTable.put(0, new JLabel(new ImageIcon("nine.gif")));
labelTeble.put(20,new JLabel(new ImageIcon("ten.gif")));

将Dictionary< Integer, Component >对象传递给setLabelTable时, 编译器会发出警告

因为编译器无法确定setLabelTable到底用Dictionary对象做什么(没有参数类型的Dictionary泛型类, 和从未更新的java5之前的Slider存在兼容性问题)

对于

Dictionary<Integer, Components> labelTable = silder.getLabelTable();

这样做会看到一个警告

确保标签表确实包含Integer和Component对象,恶意的程序员可能在滑块中安装一个不同的Dictionary, 不过这种情况并不会比有泛型之前的情况更糟, 最差的情况也就是程序抛出一个异常

考虑该警告后, 可以使用注解(annotation)使之消失

@SuppressWarnings("unchecked")
Dictionary<Integer, Components> labelTable = slider.getLableTable(); //no warning

限制与局限性

  1. 不能用基本类型实例化类型参数

  2. 运行时类型查询只适用于原始类型

  3. 不能创建参数化类型的数组, 如果需要收集参数化类型对象, 简单使用ArrayList< Pair< String > >即可

  4. Varargs警告

    @SafeVarargs
    public static <T> void addAll(Collection<T> coll, T... ts)
    

    对于任何只需要读取参数数组元素的方法都可以使用这个注解

  5. 不能实例化类型变量

    public Pair() {first = new T(); second = new T();} //ERROR
    

    该构造器非法。

    public static <T> Pair<T> makePair(Supplier<T> constr)
    {
        return new Pair<>(constr.get(), constr.get());
    }
    
    Pair<String> p = Pair<T>makePair(string::new);
    

    传统方法为通过反射调用Constructor.newInstance方法构造泛型对象。

    public static <T> Pair<T> makePair(Class<T> cl)
    {
    	try
    	{
        	return new Pair<>(cl.getConstructor().newInstance()), cl.getConstructor().newInstance());
    	}
    	catch (Exception e){return null; }
    }
    
  6. 不能构造泛型数组

    public static <T extends Comparable> T[] minmax(T...a)
    {
        T[] mm = new T[2];		//ERROR 
    }
    

    类型擦除会使该方法总是构造Comparable数组

    public static <T extends Comparable> T[] minmax(T...a)
    {
        var result  =  new Comparable[2]; 		//array of erased type
        ...
        result (T[]) result;		//compiles with warning
    }
    

    将会出现ClassCastException

    最好让用户提供一个数组构造器表达式

    String [] names = ArrayAlg.minmax(String[]::new, "Tom", "Dick", "Harry");
    

    minmax方法使用该参数生成一个有正确类型数组:

    public static <T extends Comparable> T[] minmax (IntFunction<T[]> constr, T...a)
    {
        T[] result = constr.apply(2);
        ...
    }
    

    老式方法是利用反射:

    public static <T extends Comparable> T[] minmax (T...a)
    {
        var result = (T[]) Array.newInstance(a.getClass().getComponentType(), 2); 
        ...
    }
    

    ArrayList类的toArray方法则需要生成一个T[]数组, 但没有元素类型

    有下面两种形式:

    Object[] toArray()
    T[] toArray(T[] result)
    

    第二个方法接受一个数组参数, 若数组足够大, 就使用这个数组, 否则, 用result的元素类型构造一个足够大的新数组

  7. 泛型类的静态上下文中类型变量无效

    不能在静态字段或方法中引用类型变量

    public class Singleton<T>
    {
        private static T singleInstance;		//ERROR
        private static T getSingleInstance()	//ERROR
        {
            if(singleInstance == null) construct new instance of T
                return singleInstance;
        }
    }
    
  8. 不能抛出或捕捉泛型类的实例

    泛型类扩展Throwable都不允许

    public class Problem<T> extends Exception{.....} 		//ERROR
    

    catch子句不能使用类型变量

    public static <T extends Throwable> void doWork(Class<T> t)
    {
        try
        {
            do work
        }
        catch(T e)	//ERROR can not catch type variable
        {
            Logger.globle.info(...);
        }
    }
    

    在异常规范中使用类型变量可行

    public static <T extends Throwable> void d
    {
        try
        {
            do work
        }
        catch (Throwable realCause)
        {
            t.initCause(realCause);
            throw t;
        }
    }
    
  9. 可以取消对检查型异常的检查

    @SuppressWarnings("unchecked")
    static <T extends Throwable> void throwAs(Throwable t) throws T
    {
        return (T) t;
    }
    
    try
    {
        do work
    }
    catch (Throwable t)
    {
        Task.<RuntimeException>throwAs(t);
    }
    

    利用此可以将一个检查型异常转换为非检查型异常

  10. 注意擦除后的冲突

    泛型类型被擦除后, 不允许创建引发冲突的条件

    泛型规范说明引用了另一条原则, 倘若两个接口类型是同一接口的不同参数化, 一个类或类型变量就不能同时作为这两个接口类型的子类

    class Employee implements Comparable<Employee> {...}
    class Manager extends Employee implements Comparable<Manager> {...}			//ERROR
    

    其原因与合成的桥方法产生冲突有关, 实现了Comparable< X > 的类会获得一个桥方法:

    public int compareTo(Object other) {return compareTo((X) other);}
    

    不能对不同的类型X有两个这样的方法

    泛型类型的继承规则

    Pair< Manager >与Pair < Employee >两者没有任何关系

    Manager [] topHonchos = ...;
    Pair<Employee> result = ArrayAlg.minmax(topHonchos); 		//ERROR
    

    转换成原始类型依旧会出现错误

    var managerBuddies = new Pair<Manager> (ceo, cfo);
    Pair rawBuddies = managerBuddies;
    rawBuddies.setFirst(new File("..."));	//only a compile-time warning
    

    泛型类可以拓展或实现其他泛型类

    通配符类型

    通配符概念

    Pair<? extends Employee>
    

    表示任何Pair类型的类型参数是Employee的子类

    public static void printBuddies(Pair< Employee > p)
    {
    	Employee first = p.getFirst();
        Employee second = p.getSecond();
        System.out.println(irst.getName() +  " and " + second.getName() + " are buddies.");
    }
    

    这样不能将Pair< Manager > 传递给该方法

    public static void printBuddies (Pair<? extends Employee> p)
    

    使用通配符不会通过Pair<? extends Employee> 的引用破坏Pair< Manager >

    var managerBuddies = new Pair<Manager> (ceo, cfo);
    Pair<? extends Employee> wildcardBuddies = managerBuddies;
    wildcardBuddies.setFirst(new File("..."));	//compile-time warning
    

    只能使用返回值,不能提供参数

通配符的超类型限定

? super Manager

带有超类型限定的通配符允许写入一个泛型对象, 带有子类型限定的通配符允许读取一个泛型对象。

超类型限定的另一个用法是

对于接口Comparable

public interface Comparable<T>
{
    public int compareTo(T other);
}

处理LocalDate对象数组时, 因为LocalDate实现的是Comparable< ChronoLocalDate >, 不是Comparable < LocalDate >,

在这种情况下, 可以用超类型解决。

public static <T extends Comparable<? super T>> T min (T[] a)...

无限定通配符

? getFirst()
void setFirst(?)

返回值只能赋给Object, setFirst方法无法调用,

它对于很多简单操作非常有用

public static boolean hasNulls(Pair<?> p)
{
    return p.getFirst() == null || p.getSecond() == null;
}

通过将hasNull转换为泛型方法, 可以避免使用通配符类型

public static <T> boolean hasNulls (Pair<T> p)

通配符捕获

public static void swap(Pair<?> p)

下面代码非法

? t = p.getFirst();
p.setFirst(p.getSecond());
p.setSecond(t);

可以写一个辅助方法

public static <T>  void swapHelper(Pair<T> p)
{
        T t = p.getFirst();
        p.setFirst(p.getSecond());
        p.setSecond(t);   
}
public static void swap(Pair<?> p) {swapHelper(p);}

swapHelper的方法的参数T会捕获修饰符

package pair3;

/**
 * @author Cay Horstmann
 */

public class PairTest3 
{
    public static void main(String[] args) {
        var ceo = new Manager("Gus Greedy", 800000, 2003, 12, 15);
        var cfo = new Manager("Sid Sneaky", 600000, 2003, 12, 15);
        var buddies = new Pair<Manager>(ceo, cfo);
        printBuddies(buddies);

        ceo.setBonus(1000000);
        cfo.setBonus(500000);
        Manager[] managers = {ceo, cfo};

        var result = new Pair<Employee>();
        minmaxBonus(managers, result);
        System.out.println("First: " + result.getFirst().getName() + ", second: " + result.getSecond().getName());
        maxminBonus(managers, result);
        System.out.println("First: " + result.getFirst().getName() + ", second: " + result.getSecond().getName());
    }
    public static void printBuddies(Pair<? extends Employee> p)
    {
        Employee first = p.getFirst();
        Employee second = p.getSecond();
        System.out.println(first.getName() + " and " + second.getName() + " are buddies.");
    }

    public static void minmaxBonus(Manager[] a, Pair<? super Manager> result)
    {
        if(a.length == 0) return;
        Manager min = a[0];
        Manager max = a[0];
        for (int i = 1; i < a.length; i++) 
        {
            if (min.getBonus() > a[i].getBonus())
                min = a[i];
            if(max.getBonus() < a[i].getBonus())
                max = a[i];
        }
        result.setFirst(min);
        result.setSecond(max);
    }
    public static void maxminBonus(Manager[] a, Pair<? super Manager> result)
    {
        minmaxBonus(a, result);
        PairAlg.swapHelper(result);   
    }
    //can not write public static <T super manager>...

}

class PairAlg
{
    public static boolean hasNulls(Pair<?> p) 
    {
        return p.getFirst() == null || p.getSecond() == null;   
    }
    public static void swap(Pair<?> p)
    {
        swapHelper(p);   
    }
    public static <T>  void swapHelper(Pair<T> p)
    {
        T t = p.getFirst();
        p.setFirst(p.getSecond());
        p.setSecond(t);   
    }
}

反射和泛型

泛型Class类

Class类是泛型的

API

java.lang.Class< T >

  • T
    cast(Object obj)
    

    Casts an object to the class or interface represented by this Class object.

  • public T newInstance()
    

    Deprecated.

    This method propagates any exception thrown by the nullary constructor, including a checked exception.

  • T[]
    getEnumConstants()
    

    Returns the elements of this enum class or null if this Class object does not represent an enum class.

  • Class<? super T>
    getSuperclass()
    

    Returns the Class representing the direct superclass of the entity (class, interface, primitive type or void) represented by this Class.

  • Constructor<T>
    getConstructor(Class<?>... parameterTypes)
    

    Returns a Constructor object that reflects the specified public constructor of the class represented by this Class object.

  • Constructor<?>[] 
    getConstructors()
    

    Returns an array containing Constructor objects reflecting all the public constructors of the class represented by this Class object.

  • Constructor<T> 
    getDeclaredConstructor(Class<?>... parameterTypes)
    

    Returns a Constructor object that reflects the specified constructor of the class or interface represented by this Class object.

  • Constructor<?>[] getDeclaredConstructors()
    

    Returns an array of Constructor objects reflecting all the constructors declared by the class represented by this Class object.

java.lang.reflect.Constructor< T >

  • T
    newInstance(Object... initargs)
    

    Uses the constructor represented by this Constructor object to create and initialize a new instance of the constructor’s declaring class, with the specified initialization parameters.

使用Class< T > 参数进行类型匹配

匹配泛型方法中的Class< T >的参数的类型变量会很好用

public static <T> Pair<T>  makePair(Class<T> c)
throws InstantiationException, IllegalAccessError
{
    return new Pair<>(c.getDeclaredConstructor().newInstance(), c.getDeclaredConstructor().newInstance());
}

虚拟机中的泛型类型信息

为了表述泛型类型声明, 可以使用java.lang.reflect包中的接口Type, 包含以下子类型:

  • Class类, 描述具体类型
  • TypeVariable接口, 描述类型变量(T extends Comparable<? super T>)
  • WildcardType接口, 描述通配符(? super T)
  • ParameterizedType接口, 描述泛型类或接口类型(Comparable <? super T>)
  • GenericArrayType接口, 描述泛型数组( T[] )
package genericReflection;

import java.util.*;

import java.lang.reflect.*;
/**
 * @author Cay Horstmann
 */
public class GenericReflectionTest 
{
    public static void main(String[] args) {
        //read class name from command ling args or user input
        String name;
        if(args.length > 0) name = args[0];
        else
        {
            try (var in = new Scanner(System.in))
            {
                System.out.println("Enter class name (e.g., java.util.Collections): ");
                name = in.next();
            }
        }

        try
        {
            //print generic info for class and public methods
            Class<?> cl = Class.forName(name);
            printClass(cl);
            for(Method m : cl.getDeclaredMethods())
                printMethod(m);
        }
        catch(ClassNotFoundException e)
        {
            e.printStackTrace();
        }
    }
    public static void printClass(Class<?> cl)
    {
        System.out.print(cl);
        printTypes(cl.getTypeParameters(), "<", ", " , ">", true);
        Type sc = cl.getGenericSuperclass();
        if(sc != null)
        {
            System.out.print(" extends ");
            printType(sc, false);
        }
        printTypes(cl.getGenericInterfaces(), " implements ", ", ", "", false);
        System.out.println();
    }
    public static void printTypes(Type[] types, String pre, String sep, String suf, boolean isDefinition)
    {
        if(pre.equals(" extend ") && Arrays.equals(types, new Type[] {Object.class}))
            return;
        if(types.length > 0)    System.out.print(pre);
        for(int i = 0; i < types.length; i++)
        {
            if(i > 0) System.out.print(sep);
            printType(types[i], isDefinition);
        }
        if(types.length > 0) System.out.print(suf);
    }

    public static void printMethod(Method m) 
    {
        String name = m.getName();
        System.out.print(Modifier.toString(m.getModifiers()));
        System.out.print(" ");
        printTypes(m.getTypeParameters(), "<", ", ", "> ", true);
        printType(m.getGenericReturnType(), false);
        System.out.print(" ");
        System.out.print(name);
        System.out.print("(");
        printTypes(m.getGenericParameterTypes(), "", ", ", "", false);
        System.out.println(")");

        
    }
    public static void printType(Type type, boolean isDefinition)
    {
        if(type instanceof Class)
        {
            var t = (Class<?>) type;
            System.out.print(t.getName());
        }
        else if(type instanceof TypeVariable)
        {
            var t = (TypeVariable<?>) type;
            System.out.println(t.getName());
            if(isDefinition)
                printTypes(t.getBounds(), " extends ", " & ", "", false);
        }
        else if(type instanceof WildcardType)
        {
            var t = (WildcardType) type;
            System.out.print("?");
            printTypes(t.getUpperBounds(), " extends ", " & ", "", false);
            printTypes(t.getLowerBounds(), " super", " & ", "", false);
        }
        else if(type instanceof ParameterizedType)
        {
            var t = (ParameterizedType) type;
            Type owner = t.getOwnerType();
            if(owner != null)
            {
                printType(owner, false);
                System.out.print(".");
            }
            printType(t.getRawType(), false);
            printTypes(t.getActualTypeArguments(), "<", ", ", ">", false);
        }
        else if(type instanceof GenericArrayType)
        {
            var t =(GenericArrayType) type;
            System.out.print("");
            printType(t.getGenericComponentType(), isDefinition);
            System.out.print("[]");
            
        }
        
    }
    
}

类型字面量

有时希望由值的类型决定程序的行为, 可能希望用户指定一种方法来保存某个特定类的对象, 通常实现的方法是将Class对象与一个动作关联

对于泛型类, 可以捕捉泛型接口的实例, 然后构造一个匿名子类

class TypeLiteral
{
    public TypeLiteral()
    {
        Type parentType = getClass().getGenericSuperClass();
        if(parentType instanceof ParameterizedType)
        {
            type = ((ParameterizedType) parentType).getActualTypeArguments()[0];
        }
        else
            throw new UnsupportedOperationException(
        		"Construct as new TypeLiteral<...>(){}");
    }
    ...
}

虽然对象的泛型类型已经被擦除, 但字段和方法参数的泛型类型还留存在虚拟机中。

package genericReflection;

import java.lang.reflect.*;
import java.util.*;
import java.util.function.*;



/**
 * A type literal describes a type that can be generic, such as ArrayList<String>.
 */


class TypeLiteral<T>
{
    private Type type;

    /**
     * This constructor must be invoked from an anonymous subclass.
     * as new TypeLiterral<...>(){};
     */
    public TypeLiteral()
    {
        Type parentType = getClass().getGenericSuperclass();
        if(parentType instanceof ParameterizedType)
        {
            type = ((ParameterizedType) parentType).getActualTypeArguments()[0];
        }
        else
        {
            throw new UnsupportedOperationException("Construct as new TypeLiteral<...>(){}");
        }
    }
    private  TypeLiteral(Type type) 
    {
        this.type = type;   
    }
    /**
     * Yields static literal that describes the given type.
     */
    public static TypeLiteral<?> of(Type type)
    {
        return new TypeLiteral<Object>(type);
    }

    public String toString() 
    {
        if(type instanceof Class) return ((Class<?>) type).getName();
        else return type.toString();
    }
    public boolean equals(Object otherObject) 
    {
        return otherObject instanceof TypeLiteral && type.equals(((TypeLiteral<?>) otherObject).type);
    }
    public int hashCode()
    {
        return type.hashCode();
    }
}

/**
 * Formats objects, using rules that associate types with formatting functions.
 */
class Formatter
{
    private Map<TypeLiteral<?>, Function<?, String>> rules = new HashMap<>();

    /**
     * Add a formatting rule to this formatter.
     * @param type the type to which this rule applies.
     * @param formatterForType the function that formats objects of this type
     */

     public <T> void forType(TypeLiteral<T> type, Function<T, String> formatterForType)
     {
         rules.put(type, formatterForType);
     }

     /**
      * Formats all fields of an object using the rules of this formatter.
      @param obj an object
      @return a string with all field names and formatted values
      */

      public String formatFields (Object obj)
      throws IllegalAccessException, IllegalArgumentException
      {
        var result = new StringBuilder();
        for (Field  f : obj.getClass().getDeclaredFields()) 
        {
            result.append(f.getName());
            result.append("=");
            f.setAccessible(true);
            Function<?, String> formatterForType = rules.get(TypeLiteral.of(f.getGenericType()));
            if(formatterForType != null)
            {
                //formatterForType has parameter type ?. Nothing can be passed to its apply
                //method. Cast makes the parameter type to Object so we can invoke it.
                @SuppressWarnings("unchecked")
                Function<Object, String> objectFormatter = (Function<Object, String>)formatterForType;
                result.append(objectFormatter.apply(f.get(obj)));
            }
            else
                 
            result.append("\n");
        }
        return result.toString();
      }
}

public class TypeLiterals
{
    public static class Sample
    {
        ArrayList<Integer> nums;
        ArrayList<Character> chars;
        ArrayList<String> strings;
        public Sample()
        {
            nums = new ArrayList<>();
            nums.add(42);
            nums.add(1729);
            chars = new ArrayList<>();
            chars.add('H');
            chars.add('i');
            strings = new ArrayList<>();
            strings.add("Hello");
            strings.add("World");
        }
    }

    private static <T> String join(String separator, ArrayList<T> elements)
    {
        var result = new StringBuilder();
        for(T e: elements)
        {
            if(result.length() > 0)
                result.append(separator);
            result.append(e.toString());
        }
        return result.toString();
    }
    public static void main(String[] args) 
    throws Exception
    {
        var formatter = new Formatter();
        formatter.forType(new TypeLiteral<ArrayList<Integer>>(){}, lst -> join("", lst));
        formatter.forType(new TypeLiteral<ArrayList<Character>>(){}, lst -> "\"" + join("", lst) + "\"");
        System.out.println(formatter.formatFields(new Sample()));
    }   
}

API

java.lang.Class

  • TypeVariable<Class<T>>[]
    getTypeParameters()
    

    Returns an array of TypeVariable objects that represent the type variables declared by the generic declaration represented by this GenericDeclaration object, in declaration order.

  • Type
    getGenericSuperclass()
    

    Returns the Type representing the direct superclass of the entity (class, interface, primitive type or void) represented by this Class object.

  • Type[]
    getGenericInterfaces()
    

    Returns the Types representing the interfaces directly implemented by the class or interface represented by this Class object.

java.lang.reflect.Method

  • TypeVariable<Method>[]
    getTypeParameters()
    

    Returns an array of TypeVariable objects that represent the type variables declared by the generic declaration represented by this GenericDeclaration object, in declaration order.

  • Type
    getGenericReturnType()
    

    Returns a Type object that represents the formal return type of the method represented by this Method object.

  • Type[]
    getGenericParameterTypes()
    

    Returns an array of Type objects that represent the formal parameter types, in declaration order, of the executable represented by this object.

java.lang.TypeVariable

  • String
    getName()
    

    Returns the name of this type variable, as it occurs in the source code.

  • Type[]
    getBounds()
    

    Returns an array of Type objects representing the upper bound(s) of this type variable.

java.lang.reflect.WildcardType

  • Type[]
    getLowerBounds()
    

    Returns an array of Type objects representing the lower bound(s) of this type variable.

  • Type[]
    getUpperBounds()
    

    Returns an array of Type objects representing the upper bound(s) of this type variable.

java.lang.reflect.ParameterizedType

  • Type[]
    getActualTypeArguments()
    

    Returns an array of Type objects representing the actual type arguments to this type.

  • Type
    getOwnerType()
    

    Returns a Type object representing the type that this type is a member of.

  • Type
    getRawType()
    

    Returns the Type object representing the class or interface that declared this type.

java.lang.reflect.GenericArrayType

  • Type
    getGenericComponentType()
    

    Returns a Type object representing the component type of this array.

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐