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检索通用参数 T
和 Tdto
的实际类型,然后将 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 相比保持不变。
在我从事的项目中,我必须多次创建来自其 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检索通用参数 T
和 Tdto
的实际类型,然后将 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 相比保持不变。