I have 3 files somewhat like so:
public class AConfig {
BConfig bConfig; //assume initialized
private B getA() {
return bConfig.getB(this::pickWhich);
}
public <T> Wrapper<T> pickWhich(T a, T b) {
//logic to decide whether to return a or b that doesn't actually use a or b
//store a or b in c
return new Wrapper<>(c);
}
}
.
public class BConfig {
CConfig cConfig; //assume initialized
B b1, b2; //assume initialized
public <T> B getB(BiFunction<T, T, Wrapper<T>> function) {
C c = cConfig.getC(function);
return createB(function.apply(b1, b2), c);
}
private B createB(Wrapper<B> b, C c) {
//...
}
}
.
public class CConfig {
C c1, c2; //assume initialized
public <T> C getC(BiFunction<T, T, Wrapper<T>> function) {
return createC(function.apply(c1, c2));
}
public C createC(Wrapper<C> c) {
//...
}
}
What I'm trying to do is make it so I can pass down the function and use it in both methods getB and getC, but I get errors in the apply parenthesis saying "apply (T, T) in BiFunction cannot be applied to (B, B)" and the same error in getC but with "(C, C)".
I know if I change getB to
public B getB(BiFunction<B, B, Wrapper<B>> function) {
C c = cConfig.getC(function);
return createB(function.apply(b1, b2), c);
}
It will work fine, but then I can't pass the function down through the getC call.
Is there any way to keep the function generic so it can be used in both methods?
The problem is that createB requires as an input a Wrapper<B> but the only thing that is guaranteed in the current code to have is a Wrapper<T> where T is totally unbound (can be anything).
Same with createC it would need that the input BiFunction returns a Wrapper<C>.
So the problem is whether you can construct a BiFunction that would simultaneously have a Wrapper and Wrapper return.
If T extends B and C (so is declared as <T extends B & C> This seems to
be accomplished.
However since you intent to apply the BiFunction on B and C arguments T also need to be a supper of both B and C .... so in the end T, B and C need to be the same class!!!
I think you may need to rethink your design.
Related
Suppose I have the following structure:
public interface A {
}
public interface B {
}
public interface B1 extends B {
}
public interface B2 extends B {
}
public class C implements A, B1 {
private final String s;
public C(final String s) {
this.s = s;
}
}
public class D implements A, B2 {
private final Exception e;
public D(final Exception e) {
this.e = e;
}
}
public class SomeClass<T> {
private final T t;
private final Exception e;
public SomeClass(final T t, final Exception e) {
this.t = t;
this.e = e;
}
public <U extends B> U transform(final java.util.function.Function<T, ? extends U> mapper1, final java.util.function.Function<Exception, ? extends U> mapper2) {
return t == null ? mapper2.apply(e) : mapper1.apply(t);
}
}
When now we do the following in another class:
public class AnotherClass {
public static void main(final String[] args) {
SomeClass<String> someClass = new SomeClass<>("Hello World!", null);
// this line is what is bothering me
A mappedResult = someClass.transform(C::new, D::new);
}
}
The code compiles without any problems. Why does the code compile? How is it possible that the type of 'mappedResult' can be A, even though the generic U in the method is declared to be a subtype of B?
Ok, so based on the comments on the question and some discussion with other people, there was a major point that I missed that might need addressing and that actually explains the answer given in the comments.
It's clear that the following compiles:
Object mappedResult = someClass.transform(C::new, D::new);
And yet Object is not a subclass of B, of course. The bound will ensure that the the types of C and D (in this case) will be a subtype of B, but they can be other types as well due thanks to other interfaces both C and D implement. The compiler will check what types they are and look at the most specific type(s) that they have in common. In this case, that is both A and B, so the type is derived to be A & B. Therefore, assigning this result to A is possible, because the compiler will derive the result to be an A as well.
The bound does provide some restrictions regarding the input, but not regarding the output and not regarding to the types of variables to which you can assign the result. That is what I was confused about before.
Another way to see this is the following: if the method had been defined as follows:
public <U> U transform(final java.util.function.Function<T, ? extends U> mapper1, final java.util.function.Function<Exception, ? extends U> mapper2) {
return t == null ? mapper2.apply(e) : mapper1.apply(t);
}
then the result can still be assigned to an A or a B when calling it as before. The bound had no influence on that. All it ensures here is that both mapper functions need to map to a result that is a subtype of U. With the bound, that becomes a subtype of U which is a subtype of B. But the fact that the result is a subtype of A doesn't change the fact that it is also a subtype of B. Therefore, the result can be assigned to either type.
I'm trying to understand how Either is implemented. I've gotten stuck at chaining together multiple functions in a way that allows returning a different Left value during flatMap. I can't work out how it is possible within the type system.
Minimal Either example code:
public class Either<A,B> {
public final A left;
public final B right;
private Either(A a, B b) {
left = a;
right = b;
}
public static <A, B> Either<A, B> left(A a) {
return new Either<>(a, null);
}
public static <A, B> Either<A, B> right(B b) {
return new Either<>(null, b);
}
public <C> Either<A, C> flatMap(Function<B, Either<A,C>> f) {
if (this.isRight()) return f.apply(this.right);
else return Either.left(this.left);
}
// map and other useful functions....
I originally thought I'd be able to map to different Left values, which would allow returning the relevant error at each point.
So, for instance, given these functions:
public static Either<Foo, String> doThing() {
return Either.right("foo");
}
public static Either<Bar, String> doThing2(String text) {
return (text.equals("foo"))
? Either.right("Yay!")
: Either.left(new Bar("Grr..."));
}
public static Either<Baz, String> doThing3() {
return (text.equals("Yay!"))
? Either.right("Hooray!")
: Either.left(new Baz("Oh no!!"));
}
I thought I'd be able to do
doThing().flatMap(x -> doThing2()).flatMap(y -> doThing3())
However, the compiler flags this as impossible.
After some studying of the code, I realized that it's due to my <A,B> generic parameters.
flatMap has two different cases:
the case where we map the right side
the case where we pass through the left value
So, if my goal is to enable sometimes returning different Left values from flatMap, then my two generic variables <A,B> don't work, because if case 1 executes and my function changes A, then case 2 is invalid, because A != A'. The act of applying a function to the right side may have changed the Left side to a different type.
All this leads me to these questions:
Is my expectation for the behavior of the Either type incorrect?
Is it possible to return different Left types during a flatMap operation?
if so, how do you get the types to work out?
There isn't a sensible flatMap() function like you want, due to parametricity. Consider:
Either<Foo, String> e1 = Either.left(new Foo());
Either<Bar, String> e2 = foo.flatMap(x -> doThing2());
Bar bar = e2.left; // Where did this come from???
flatMap() itself would have had to invent a Bar instance somehow. If you start writing a flatMap() that can change both types, you'll see the issue more clearly:
public <C, D> Either<C, D> flatMap(Function<B, Either<C, D>> f) {
if (this.isRight()) {
return f.apply(this.right);
} else {
// Error: can't convert A to C
return Either.left(this.left);
}
}
You can, but your old Left has to be a subtype of or equal to the new Left, so it can be cast up. I'm not very familiar with Java's syntax, but the Scala implementation looks like:
def flatMap[A1 >: A, B1](f: B => Either[A1, B1]): Either[A1, B1] = this match {
case Right(b) => f(b)
case _ => this.asInstanceOf[Either[A1, B1]]
}
Here the A1 >: A designates A as a subtype of A1. I know Java has an <A extends A1> syntax, but I'm not sure it can be used to describe the constraint on A1, as we need in this case.
Regarding your usage of Either (doThing(...)) it seems your flat mapping isn't sufficient. I assume you want your flat mapping work like for Optional<T>.
The mapper ofOptional.flatMap takes a value of kind of T and returns an Optional<U> where U is a generic type parameter of this method. But Optional has one generic type parameter T whereas Either has two: A and B. So if you want to flat map an Either<A,B> either it isn't sufficient to use one mapping.
One mapping what should it map? "The value which isn't null" you would say - wouldn't you? Ok but you know that first at runtime. Your flatMap method is defined at compile time. Therefore you have to provide a mapping for each case.
You choose <C> Either<A, C> flatMap(Function<B, Either<A, C>> f). This mapping uses a value of type B as input. That means if the mapped Either either is !either.isRight() all following mappings would return an Either.left(a) where a is the value of the very first Either.left(a). So actually only an Either either where either.isRight() could be mapped to another value. And it has to be either.isRight() from the beginning. This means also that once an Either<A,B> either is created all flat mappings will result in a kind of Either<A,?>. So the current flatMap restricts an Either either to keep its left generic type. Is this what you supposed to do?
If you want to flat map an Either either without restrictions you need mappings for both cases: either.isRight() and !either.isRight(). This will allow you to continue the flat mapping in both directions.
I did it this way:
public class Either<A, B> {
public final A left;
public final B right;
private Either(A a, B b) {
left = a;
right = b;
}
public boolean isRight() {
return right != null;
}
#Override
public String toString() {
return isRight() ?
right.toString() :
left.toString();
}
public static <A, B> Either<A, B> left(A a) {
return new Either<>(a, null);
}
public static <A, B> Either<A, B> right(B b) {
return new Either<>(null, b);
}
public <C, D> Either<C, D> flatMap(Function<A, Either<C, D>> toLeft, Function<B, Either<C, D>> toRight) {
if (this.isRight()) {
return toRight.apply(this.right);
} else {
return toLeft.apply(this.left);
}
}
public static void main(String[] args) {
Either<String, String> left = Either.left(new Foo("left"))
.flatMap(l -> Either.right(new Bar(l.toString() + ".right")), r -> Either.left(new Baz(r.toString() + ".left")))
.flatMap(l -> Either.left(l.toString() + ".left"), r -> Either.right(r.toString() + ".right"));
System.out.println(left); // left.right.right
Either<String, String> right = Either.right(new Foo("right"))
.flatMap(l -> Either.right(new Bar(l.toString() + ".right")), r -> Either.left(new Baz(r.toString() + ".left")))
.flatMap(l -> Either.left(l.toString() + ".left"), r -> Either.right(r.toString() + ".right"))
.flatMap(l -> Either.right(l.toString() + ".right"), r -> Either.left(r.toString() + ".left"));
System.out.println(right); // right.left.left.right
}
private static class Foo {
private String s;
public Foo(String s) {
this.s = s;
}
#Override
public String toString() {
return s;
}
}
private static class Bar {
private String s;
public Bar(String s) {
this.s = s;
}
#Override
public String toString() {
return s;
}
}
private static class Baz {
private String s;
public Baz(String s) {
this.s = s;
}
#Override
public String toString() {
return s;
}
}
}
Answering your question: Yes it is possible to construct an Either returning a different left value. But I think your intent was to know how to get a proper working Either.
I have a ModelDecorator helper. I want it to have the following public API
class ModelDecorator<T>{
public static <T> ModelDecorator<T> create(Class<T> clazz);
public <SUPER> T from(SUPER fromInstance);
}
So, given classes A, B extends A, it can be used like this:
A a = new A();
B b = ModelDecorator.create(B.class).from(a);
But I want to have bounds on T and SUPER, so I make sure that only subclases can be instantiated using the API. At this moment, I can do:
C c = new C();
B b = ModelDecorator.create(B.class).from(c);
Where B DOES not inherit from C.
Any ideas? Thanks
The only way I see for constraining your type parameter T to extend the type parameter S is to put those definitions into the class definition:
public class ModelDecorator<S, T extends S> {
public static <S, T extends S> ModelDecorator<S, T> create(Class<T> clazz) { ... }
public T from(S instance) { ... }
}
With these classes
class A {}
class B extends A {}
class C {}
you now can write the following code:
A a = new A();
B b1 = ModelDecorator.<A, B> create(B.class).from(a); // compiles fine
C c = new C();
B b2 = ModelDecorator.<C, B> create(B.class).from(c); // bound mismatch here
The second B creation now has a compiler error.
Unfortunately you now must explicitely provide the type parameters because the compiler is not able to infer the type A or C from a simple method call create(B.class).
I have a base class with a method called execute :
class A {
public execute(int a){}
}
I also have a class B, which extends A, but the execute method needs more parameters:
Currently, my solution is using optional parameters :
class B extends A {
public execute(int a, Object... parameters){
long b = (long)parameters[0];
boolean c = (boolean)parameters[1];
....
}
}
This would still be ugly because I must cast on parameters. Are there other options for this situation?
you can add an execute(int a, int b) in B, but it won't override the execute(int a) method, it will overload it. Both method will be callable on an instance of B.
This would break the OO paradigm. The L in solid stands for Liskov substitution principle.
The principle applied for you example is that B should behave as A.
A better solution would be to injects those parameters via the constructor and have an execute without any parameters.
class A {
int a;
public A(int a){
this.a = a;
}
public execute(){ // do something with a}
}
class B {
int a;
long b;
boolean c;
public B (int a, long b, boolean c) {
this.a = a;
this.b = b;
this.c = c;
}
public execute(){ // do something with a, b and c}
}
This is just a theorical question with no concrete application.
I have the following method which I will not touch. It could (if possible at all) be used as a BiConsumer.
void doSmallThing(A a, B b) {
// do something with a and b.
}
void doBigThing(List<A> as, B b) {
// What to do?
}
How can I iterate on as while keeping b constant and use this::doSmallThing in doBigThing?
Of course the following doesn't work.
void doBigThing(List<A> as, B b) {
as.stream()
.forEach(this::doSmallThing);
}
The following works nice and is actually what I use everyday.
void doBigThing(List<A> as, B b) {
as.stream()
.forEach(a -> doSmallThing(a, b));
}
The following also works well, but is a bit more tricky.
Consumer<A> doSmallThingWithFixedB(B b) {
return (a) -> doSmallThing(a, b);
}
void doBigThing(List<A> as, B b) {
as.stream()
.forEach(doSmallThingWithFixedB(b))
}
But all of those solutions don't get the simplicity of the Consumer case. So is there anything simple that exists for BiConsumer?
You want to "bind" the function argument. Unfortunately there's no built-in mechanism to do this in Java 8 (except binding the object for instance methods like this::). You may generalize your doSmallThingWithFixedB method like this:
public class Bind {
public static <A, B> Consumer<A> bindLast(BiConsumer<A, B> fn, B b) {
return a -> fn.accept(a, b);
}
public static <A, B> Consumer<B> bindFirst(BiConsumer<A, B> fn, A a) {
return b -> fn.accept(a, b);
}
}
And use:
void doBigThing(List<A> as, B b) {
as.stream()
.forEach(Bind.bindLast(this::doSmallThing, b));
}
Probably there's some third-party library which already contains such methods. However using explicit lambda seems ok for me. You should not try to express everything with method references.
BiConsumers are used when iterating over Map entries for example:
Map<A, B> map = ...;
map.forEach(this::doSomething);
Stream.collect() also takes BiConsumers as arguments, but it's used less often than an iteration on map entries.
Add a method doSmallThingto B:
class B {
public void doSmallThing(A a) {
YourClass.doSmallThing(a, this); // You may want to inline this.
}
}
and call it from doBigThing:
void doBigThing(List<A> as, B b) {
as.stream()
.forEach(b::doSmallThing);
}