Receives an instance of a functional interface that creates a T-type instance as an argument.
// newT()Methods that require
<T> T doSomething(Supplier<T> supplier) {
T instance = supplier.get();
return instance;
}
//Method to call doSomething
void caller() {
Test instance = this.doSomething(() -> new Test());
}
Sometimes you know that new T ()
is an anti-pattern in the first place, but you want to do new T ()
.
The following patterns are often presented as solutions at that time.
<T> T doSomething(Class<T> clazz) throws InstantiationException, IllegalAccessException {
T instance = clazz.newIntance();
return instance;
}
void caller() {
Test instance = this.doSomething(Test.class);
}
The problem with this code is that it throws a checked exception.
Throw ʻInstantiationException and ʻIllegalAccessException
as in the code above.
I think it's a reasonable design for the Class.newInstance ()
side, but there are times when the user can be confident that the constructor won't throw an exception ...
Then, it is troublesome to bother to try-catch
or throws
the above exception.
Furthermore, since it is a check exception, it is incompatible with stream
[^ 1], and try-catch
is required internally.
<T extends SuperTest> List<T> doSomething(Class<T> clazz, List<String> valueList) throws InstantiationException, IllegalAccessException {
return valueList.stream()
.map(value -> {
//I want to write smartly with stream, but try-catch…
try {
T obj = clazz.newInstance();
obj.setValue(value);
} catch (Exception e) {
throw new RuntimeException(e);
}
})
.collect(Collectors.toList());
}
In that respect, the functional interface does not generate extra checked exceptions, so it is relatively clean (unless the constructor has throws
).
[^ 1]: To be exact, it is incompatible with the functional interface.
If the constructor takes an argument, use an instance of Function <T, R>
.
If there are two, BiFunction <T, U, R>
.
If there are 3 or more, create a class for the argument or use java.util.Map
.
1 argument
// newT()Methods that require
<T> T doSomething(Function<String, T> func) {
T instance = func.apply("Constructor arguments");
return instance;
}
//Method to call doSomething
void caller() {
Test instance = this.doSomething((str) -> new Test(str));
}
Two arguments
// newT()Methods that require
<T> T doSomething(BiFunction<String, Integer, T> func) {
T instance = func.apply("Constructor arguments", 1);
return instance;
}
//Method to call doSomething
void caller() {
Test instance = this.doSomething((str, number) -> new Test(str, number));
}
Recommended Posts