Java: 使用非默认构造函数实例化泛型对象

Java: Instantiate generic object with non-default constructor

在我从事的项目中,我必须多次创建来自其 DTO 的不同对象。尽量遵循不重复代码的原则,我尝试创建一个通用的class这样的:

public class AssembleObjectFromDto<T,Tdto> {
    public List<T> tFromDto(List<Tdto> tdtoList){
        List<T> tList = new ArrayList<>();
        for(Tdto tdto : tdtoList){
            tList.add(new T(tdto));
        }
        return tList;
    }
}

但是,我不能直接实例化通用对象。

我想知道,我还能用什么其他方法来解决这个问题。

提前致谢

你不能那样做,因为编译器和运行时都不知道 T 和 Tdto 的具体类型,因此甚至不知道是否存在这样的构造函数。

但您可以传递 Function<Tdto, T> 代替:

public List<T> tFromDto(List<Tdto> tdtoList, Function<Tdto, T> constructor){
    List<T> tList = new ArrayList<>();
    for(Tdto tdto : tdtoList){
        tList.add(constructor.apply(tdto));
    }
    return tList;
}

您将通过以下方式调用:

List<Foo> dtos = assemble.tFromDto(fooDTOs, Foo::new)

请注意,此方法不需要在泛型中 class。它可以是通用的,也可以是静态的(并使用流进行简化):

public static <T, D> List<T> fromDTOs(List<D> dtoList, Function<D, T> constructor){
    return dtoList.stream().map(constructor).collect(Collectors.toList());
}

鉴于此方法的简单性,您甚至可以将其完全删除。

实际上,不可能使用非默认构造函数实例化通用对象是不正确的。

免责声明:我并不是说这是最好的方法,特别是因为它涉及反射。您可能需要考虑使用 JB Nizet 建议的简化静态版本。然而,假设这只是一个最小的工作玩具示例,我将根据 OP 代码回答最初的问题,并表明确实可以使用非默认构造函数构造通用对象。

版本 1,直接使用通用程序集class

AssembleObjectFromDto.java:

import java.util.List;
import java.util.Arrays;
import java.util.ArrayList;
import java.lang.NoSuchMethodException;
import java.lang.InstantiationException;
import java.lang.IllegalAccessException;
import java.lang.reflect.InvocationTargetException;
import static java.lang.System.out;

class AssembleObjectFromDto<T, Tdto>
{
    public List<T> tFromDto(Class<T> clsT, Class<Tdto> clsTdto, List<Tdto> tdtoList)
        throws NoSuchMethodException, InstantiationException,
               IllegalAccessException, InvocationTargetException
    {
        List<T> tList = new ArrayList<>();
        for(Tdto tdto : tdtoList)
            tList.add(clsT.getConstructor(clsTdto).newInstance(tdto));
        return tList;
    }

    public static void main(String[] args)
    {
        AssembleObjectFromDto<Obj, Dto> assembler = new AssembleObjectFromDto<>();
        List<Dto> lstDto = Arrays.asList(new Dto(), new Dto(), new Dto(), new Dto());
        try
        {
            List<Obj> lstObj = assembler.tFromDto(Obj.class, Dto.class, lstDto);
            for(Obj o : lstObj)
                System.out.println(o.getClass().getName());
        }
        catch(NoSuchMethodException | InstantiationException | IllegalAccessException |
              InvocationTargetException ex)
        {
            System.out.println(ex);
        }
    }
}

Dto.java:

class Dto {}

Obj.java:

class Obj
{
    private Dto dto;

    public Obj(Dto dto)
    {
        this.dto = dto;
    }
}

版本 2,使用 generic/template 专业化:

为了避免将前两个 Class 参数传递给 tFromDto(Class<T>, Class<Tdto>, List<Tdto>),可以使 AssembleObjectFromDto 成为抽象 class 定义重载 tFromDto(List<Tdto>) 和两个抽象 getter检索通用参数 TTdto 的实际类型,然后将 class 专门化为所有必需的类型。这应该使代码更具可读性,但代价是拥有更多 classes。此外,专业化应该易于使用代码生成工具实现自动化。

AssembleObjectFromDto.java:

import java.util.List;
import java.util.ArrayList;
import java.lang.NoSuchMethodException;
import java.lang.InstantiationException;
import java.lang.IllegalAccessException;
import java.lang.reflect.InvocationTargetException;

abstract class AssembleObjectFromDto<T, Tdto>
{
    protected abstract Class<T> getProductClass();
    protected abstract Class<Tdto> getAssemblerClass();

    public List<T> tFromDto(List<Tdto> tdtoList)
        throws NoSuchMethodException, InstantiationException,
               IllegalAccessException, InvocationTargetException
    {
        return tFromDto(getProductClass(), getAssemblerClass(), tdtoList);
    }

    private List<T> tFromDto(Class<T> clsT, Class<Tdto> clsTdto, List<Tdto> tdtoList)
        throws NoSuchMethodException, InstantiationException,
               IllegalAccessException, InvocationTargetException
    {
        List<T> tList = new ArrayList<>();
        for(Tdto tdto : tdtoList)
            tList.add(clsT.getConstructor(clsTdto).newInstance(tdto));
        return tList;
    }
}

AssembleObjFromDto.java:

import java.util.List;
import java.util.Arrays;
import static java.lang.System.out;
import java.lang.NoSuchMethodException;
import java.lang.InstantiationException;
import java.lang.IllegalAccessException;
import java.lang.reflect.InvocationTargetException;

class AssembleObjFromDto extends AssembleObjectFromDto<Obj, Dto>
{
    @Override
    protected Class<Obj> getProductClass() { return Obj.class; }

    @Override
    protected Class<Dto> getAssemblerClass() { return Dto.class; }

    public static void main(String[] args)
    {
        AssembleObjFromDto assembler = new AssembleObjFromDto();
        List<Dto> lstDto = Arrays.asList(new Dto(), new Dto(), new Dto(), new Dto());
        try
        {
            List<Obj> lstObj = assembler.tFromDto(lstDto);
            for(Obj o : lstObj)
                System.out.println(o.getClass().getName());
        }
        catch(NoSuchMethodException | InstantiationException | IllegalAccessException |
              InvocationTargetException ex)
        {
            System.out.println(ex);
        }
    }
}

Dto.java,Obj.java: 与版本 1 相比保持不变。