如何在Java类中声明数量可变的泛型变量并实现对应构造器?
Java类中声明数量可变的泛型变量的正确姿势
嘿,你这个问题我之前在做RecyclerView Adapter结合Data Binding的时候也踩过坑——Java的语法规则里,一个方法或者构造器只能有一个可变参数(...形式的参数),而且必须放在参数列表的最后一位,所以你原来写的那个带两个可变参数的构造器是没法编译通过的,编译器根本不知道怎么区分传入的参数哪些属于A...,哪些属于B...。
下面给你几个可行的解决方案,适配你需要传入任意数量A和B类型参数的场景:
方案一:将两组参数打包成数组传入
把可变参数改成普通数组,这样构造器就能接受两组任意数量的参数了,缺点是调用时需要手动创建数组:
public abstract class MyClass { // 把可变参数换成数组 public <A extends Alpha, B extends Beta> MyClass(A[] clses, B[] bClses) { for (A a : clses) { storeAsFieldInMyClass(a); } for (B b : bClses) { storeAsFieldInMyClass(b); } } } // 调用示例 MyClass myInstance = new MyClassImpl( new Alpha[]{alphaObj1, alphaObj2}, new Beta[]{betaObj1, betaObj2, betaObj3} );
方案二:用静态工厂方法简化调用
如果觉得手动创建数组麻烦,我们可以用静态工厂方法结合集合+可变参数的组合,既保留调用的简洁性,又符合Java语法:
import java.util.Arrays; import java.util.Collection; import java.util.Collections; public abstract class MyClass { // 构造器改用集合+单个可变参数(符合语法规则) protected <A extends Alpha, B extends Beta> MyClass(Collection<A> aCollection, B... bArgs) { for (A a : aCollection) { storeAsFieldInMyClass(a); } for (B b : bArgs) { storeAsFieldInMyClass(b); } } // 静态工厂方法:接受A类型的集合和任意数量的B类型参数 public static <A extends Alpha, B extends Beta> MyClass create(Collection<A> aCollection, B... bArgs) { // 这里返回你的具体实现类实例,比如MyClassImpl return new MyClassImpl(aCollection, bArgs); } // 重载工厂方法:支持直接传入任意数量的A类型参数(自动转成集合) @SafeVarargs public static <A extends Alpha> MyClass create(A... aArgs) { return create(Arrays.asList(aArgs)); } // 重载工厂方法:支持只传入任意数量的B类型参数 @SafeVarargs public static <B extends Beta> MyClass create(B... bArgs) { return create(Collections.emptyList(), bArgs); } } // 具体实现类示例 class MyClassImpl extends MyClass { public <A extends Alpha, B extends Beta> MyClassImpl(Collection<A> aCollection, B... bArgs) { super(aCollection, bArgs); } } // 调用示例,怎么方便怎么来: // 1. 同时传A和B MyClass instance1 = MyClass.create(Arrays.asList(alpha1, alpha2), beta1, beta2); // 2. 只传A MyClass instance2 = MyClass.create(alpha1, alpha2, alpha3); // 3. 只传B MyClass instance3 = MyClass.create(beta1, beta2);
这里的@SafeVarargs注解是用来消除泛型可变参数带来的编译警告,告诉编译器这个方法是类型安全的。
方案三:如果A和B有共同父类/接口,合并可变参数(可选)
如果Alpha和Beta都继承自同一个父类(比如BaseType),或者实现了同一个接口,那可以直接用一个可变参数接受所有实例:
public abstract class MyClass { public <T extends BaseType> MyClass(T... args) { for (T t : args) { storeAsFieldInMyClass(t); } } }
不过这个方案只适用于A和B有共同类型约束的场景,如果你需要严格区分A和B的处理逻辑,那还是用前两个方案更合适。
结合你提到的RecyclerView.Adapter+Data Binding场景,方案二的静态工厂方法会特别好用——你可以把不同类型的ViewDataBinding子类作为A和B传入,在构造器里统一处理它们的存储或初始化逻辑,调用起来也很简洁。
内容的提问来源于stack exchange,提问作者bycfly
相关产品推荐
相关产品推荐

