TL;TR
The story that it is convenient to make such a thing
Overview version.java
public class OneOf<A, B> {
final Optional<A> a;
final Optional<B> b;
public <C> C apply(Function<A, C> AtoC, Function<B, C> BtoC) {
if (a.isPresent()) {
return AtoC.apply(a.get());
} else {
return BtoC.apply(b.get());
}
}
}
There are times when you want to create one of two different class instances from an input. (For example, when you want to read a log file or form in which various records are aggregated and create another instance for each content.) You may also want to change those different instances to a single class instance for the final aggregation. (For example, when you want to convert the contents of an instance to SolrInputDocument in order to plunge it into NoSQL.)
For example, if you want to create an instance of SecondObjectA class (access log) and SecondObjectB class (purchase log) from FirstObject class (each line of log file) and convert it to FinalObject class (SolrInputDocument), the code will be like this.
FirstObject firstObject = getFirstObject();
FinalObject finalObject;
if(firstObject.isTypeA()){
SecondObjectA secondObjectA = transformA(first);
finalObject = finalTransformA(secocndObjectA);
}else{
SecondObjectB secondObjectB = transformB(first);
finalObject = finalTransformB(secocndObjectB);
}
The best solution is to change the class design so that SecondObjectA and SecondObjectB have a common parent class (or interface) SecondObject and SecondObject FirstObject.transform ().
FirstObject firstObject = getFirstObject();
SecondObject secondObject = firstObject.transform();
FinalObject finalObject = finalTransform(secondObject);
However, this is often not the case in the world. There are many cases where the class design becomes complicated, the problem of double inheritance prevents inheritance, and the class has to be used. This article discusses possible workarounds in such cases.
Let's look at the problem when we go back to the first code.
Repost.java
FirstObject firstObject = getFirstObject();
FinalObject finalObject;
if(firstObject.isTypeA()){
SecondObjectA secondObjectA = transformA(first);
finalObject = finalTransformA(secocndObjectA);
}else{
SecondObjectB secondObjectB = transformB(first);
finalObject = finalTransformB(secocndObjectB);
}
--The description is quick --transformA and transformB are functionally parallel but far away --It's hard to keep track of the code when the inside of the if, else block gets bigger.
As a solution when SecondObjectA and SecondObjectB do not have a common interface or parent class, you may find a way to create a bean with both instances.
public class MyBean {
private SecondObjectA secondObjectA;
private SecondObjectB secondObjectB;
//Getter below/lined up with setter
}
This way, the calling part can be written as if we had introduced a common parent class. But
――NPE usually occurs from such a place (although it can be dealt with by making it Immutable, hiding the constructor so that it has a unique value, etc.) --It's easy to create bad code like if (myBean.secondObjectA! = Null) {hogeuhga ~} else {} at the conversion method. --A large number of bean classes are created when there are multiple pairs that "have one of the two instances".
And it becomes a hotbed that causes a bad phenomenon.
If you make something like this
OneOf.java
public class OneOf<A, B> {
private final Optional<A> a;
private final Optional<B> b;
private OneOf(A a, B b) {
this.a = Optional.ofNullable(a);
this.b = Optional.ofNullable(b);
}
public static <A, B> OneOf<A, B> OneOfA(A a) {
return new OneOf<A, B>(a, null);
}
public static <A, B> OneOf<A, B> OneOfB(B b) {
return new OneOf<A, B>(null, b);
}
public Optional<A> getA() {
return a;
}
public Optional<B> getB() {
return b;
}
public boolean isA() {
return a.isPresent();
}
public boolean isB() {
return !isA();
}
public <C> C apply(Function<A, C> AtoC, Function<B, C> BtoC) {
if (isA()) {
return AtoC.apply(a.get());
} else {
return BtoC.apply(b.get());
}
}
public static <A, B, C> Function<OneOf<A, B>, C> totalize(Function<A, C> AtoC, Function<B, C> BtoC) {
return (one) -> one.apply(AtoC, BtoC);
}
}
You can write like this.
Main part.java
FirstObject firstObject = getFirstObject();
OneOf<SecondObjectA,SecondObjectB> secondObject = transform(first);
FinalObject finalObject = secondObject.apply(this::finalTransformA,this::finalTransformB);
Alternatively, the first object may be passed in a stream. In such a case
Main part.java
Stream<FirstObject> firstObject = getFirstObjectStream();
Stream<FinalObject> firstObject.map(this::transform).map(OneOf.totalize(this::finalTransformA,this::finalTransformB));
Like this.
If you introduce the OneOf class like this, it may be neat and simple. Since it can be used without worrying about the relationship between classes, it can also be used for existing classes, which is often convenient. However, if you need "one of the two homebrew classes", be aware that the class design may not be good. (If you use it effectively even in your own class, it will be effective. Poor vocabulary)
Recommended Posts