我可以将循环作为参数传递给构造函数吗

时间:2018-09-22 07:11:26

标签: java constructor

我有一个数组列表,其中包含构造函数所需的所有参数。引用数组中的每个项目然后分别提供参数似乎是很多工作。我想知道是否可以通过在构造函数的括号内对其进行迭代来传递数组列表中的每个项目。

我在问我是否可以这样做,或者类似的方法来传递参数。

constructor object =new constructor(for(String item: parts));

parts是此处的数组列表。列表中的所有项目都是字符串。

4 个答案:

答案 0 :(得分:0)

///我已经将您的类“构造函数”重命名为“ MyClass”,因为前者可能会引起混淆

Java中没有直接的语法可以执行此操作。但是在工厂的帮助下,您可以实现以下目标:

final String[] args = {"one", "two", "three"};
final MyClass obj = newMyClass(args); // effect of new Constructor(args[0], args[1], args[2])
final MyClass obj = newMyClass(args[0], args[1], args[2]); // works too, thanks to the variadic declaration syntax

为此,您将需要一个工厂,该工厂要么具有基于注释处理器的代码生成功能,要么需要在应用程序初始化时借助sun.misc.Unsafe构建(这是某些高性能序列化程序的工作方式)。 / p>

示例:

package net.bobah.uf4j;

import org.junit.Test;
import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiConsumer;

import static java.lang.System.out;
import static net.bobah.uf4j.MyTest.MyClassFactory.newMyClass;

public class MyTest {

    @Test
    public void runTest() throws Exception {
        final MyClass ctor = newMyClass("one", "two", "three");
        out.println(Arrays.asList(ctor.str1, ctor.str2, ctor.str3));
        // prints [one, two, three]
    }

    public static class MyClass {
        private final String str1;
        private final String str2;
        private final String str3;

        public MyClass(String str1, String str2, String str3) {
            this.str1 = str1;
            this.str2 = str2;
            this.str3 = str3;
        }

    }

    public static class MyClassFactory {
        private static Unsafe getUnsafe() {
            try {
                Field f = Unsafe.class.getDeclaredField("theUnsafe");
                f.setAccessible(true);
                return (Unsafe) f.get(null);
            } catch (IllegalAccessException | NoSuchFieldException cause) {
                throw new RuntimeException(cause);
            }
        }

        private static final Unsafe unsafe = getUnsafe();

        private static final List<BiConsumer<MyClass, String>> fieldInitializers = new ArrayList<>();

        static {
            // this can be extended to support one-to-many mappings from args to fields, or to do type transformation on the way
            for (Field field : MyClass.class.getDeclaredFields()) {
                if (String.class.equals(field.getType())) {
                    final long offset = unsafe.objectFieldOffset(field);
                    fieldInitializers.add((obj, val) -> {
                        unsafe.putObject(obj, offset, val);
                    });
                }
            }
        }

        public static MyClass newMyClass(String... args) throws InstantiationException {
            assert args.length == fieldInitializers.size();

            // create uninitialized instance
            final MyClass obj = (MyClass) unsafe.allocateInstance(MyClass.class);

            // inject final fields
            for (int i = 0; i < args.length; ++i) {
                fieldInitializers.get(i).accept(obj, args[i]);
            }

            return obj;
        }
    }

}

答案 1 :(得分:0)

假设您的constructor声明如下:

public contructor(String s1, String s2, String s3, String s4)

您的想法不是解决此问题的正确方法。如果parts没有完全4个元素怎么办?您想要的是一个包含整个列表的重载构造函数:

public constructor(List<String> strings)
{
    this(strings.get(0), strings.get(1), strings.get(2), strings.get(3));
}

这样,您可以在许多具有不同列表的不同位置调用构造函数,但只需要在一个位置“解构”列表(重载的构造函数)。例如:

constructor a = new constructor(list1);
constructor b = new constructor(parts);

constructor c1 = new constructor(Arrays.asList("1", "2", "3", "4");
// same as:
constructor c2 = new constructor("1", "2", "3", "4");

请注意,该示例仅适用于列表中的至少4个元素:

constructor e1 = new constructor(Arrays.asList("foo", "bar")); // will error
constructor e2 = new constructor(Collections.emptyList()); // will error

答案 2 :(得分:0)

听起来像是您在寻找其他编程语言中的东西,例如Groovy,称为胁迫,即列出所需类型的列表。

在Java 10之前的版本中这是不可能的,但是谁知道未来会怎样。

答案 3 :(得分:-1)

您的问题带来的问题多于答案:如果您有一个String数组或列表,那么让您的构造函数使用该数组/列表。

如果您需要将此数组/列表的内容转换为更多对象,则答案为Stream,则不需要像反射这样的复杂事情:

String[] myArray = ...;
MyType[] a = Arrays.stream(myArray).map(MyType::new).toArray(MyType[]::new);

Collection<String> coll = ...;
List<MyType> b = coll.stream().map(MyType::new).collect(Collectors.toList());

您当然可以做得更多:如果您有一个构造函数,其Arity为3(例如:3 String):

  • 我们首先创建一个[0,size()/ arity]的流,这是您需要构造的对象数。将其乘以Arity即可得到要传递给构造函数的第一项的索引。
  • 我们将index映射到MyObjsafeGet负责从列表中提取值(它也可以用于数组,但是您只需要使用它即可) length[])。
  • MyObj有一个构造函数用于接收每个元组。如果构造函数需要将源对象(字符串)中的一个转换为另一个(例如,整数),那么您最好使用静态方法valueOf对对象进行字符串转换。
  • Stream返回数组,但您可以将collect(toList())用于列表。

这是第一个示例:

  public static void main(final String[] args) {

    final List<String> list = Arrays.asList("A", "B", "C", "D", "E", "F");
    // fails if list.size() is not multiple of 3
    final int arity = 3;
    final MyObj[] myObj = IntStream.range(0, list.size() / arity).map(n -> arity * n)
    .mapToObj(index -> new MyObj(safeGet(list, index, 0), safeGet(list, index, 1), safeGet(list, index, 2)))
    .toArray(MyObj[]::new)
    ;

  }

  private static <T> T safeGet(final List<T> list, final int baseIndex, final int relativeIndex) {
    final int index = baseIndex + relativeIndex;
    return index < list.size() ? list.get(index) : null;
  }

  static class MyObj {
    public MyObj(final String a, final String b, final String c) {
    }
  }

下面是第二个示例,其中构造函数接受int而不是String

  public static void main(final String[] args) {

    final List<String> list = Arrays.asList("A", "0", "C", "D", "2", "F");
    // fails if list.size() is not multiple of 3
    final int arity = 3;
    final MyObj[] myObj = IntStream.range(0, list.size() / arity).map(n -> arity * n)
        .mapToObj(index -> MyObj.valueOf(list, index))
        .toArray(MyObj[]::new)
        ;

    Arrays.stream(myObj).forEach(System.out::println);
  }

  private static <T> T safeGet(final List<T> list, final int baseIndex, final int relativeIndex) {
    final int index = baseIndex + relativeIndex;
    return index < list.size() ? list.get(index) : null;
  }

  static class MyObj {
    public MyObj(final String a, final int b, final String c) {
    }

    public static MyObj valueOf(final List<String> source, final int baseIndex) {
      final String a = safeGet(source, baseIndex, 0);
      final int b = Integer.parseInt(safeGet(source, baseIndex, 1));
      final String c = safeGet(source, baseIndex, 2);
      return new MyObj(a, b, c);

    }

  }