0

I'm having a strange behaviour with Generics I'm using Java8.

Here is a small sample code to demonstrate the problem Following code works fine without any issues with type inference. where, SpecificError is a subtype of GenericError.

public GenericType<AbstractError> method{ Optional<SpecificError> error = Optional.of(new SpecificError()); if (error.isPresent()) { return GenericType.error(error.get()); } else { // return something else } } 

I have lot of this places in the code, where I had to do this if/else checks with optional, I decided to make a new function which receives the Optional, checks for presence and returns the Generic type object New function code:

public static <R extends AbstractError> GenericType<R> shortcut(Optional<R> error) { if (error.isPresent()) { return GenericType.error(error.get()); } else { // something else } } 

This is new code calling the above function:

public GenericType<AbstractError> method{ Optional<SpecificError> error = Optional.of(new SpecificError()); return GenericType.shortcut(error); } 

And strangely this does not work and breaks the following compilation error:

[ERROR] inferred: AbstractError [ERROR] equality constraints(s): AbstractError, SpecificError 

I just do not understand, why this won't work. The only thing, I have done is to make a small function which the job of doing isPresent check on Optional, everything else is the same. Why can't Java see that SpecificError is subtype of AbstractError

6
  • at which line is the error coming? Commented May 12, 2020 at 17:50
  • error is at the line: return GenericType.shortcut(error); I have the feeling that shortcut method is returning Explicity subtype (SpecificError), but the expected return type of the function is AbstractError, but for me, this should work, as this is what inheritance is all about? Commented May 12, 2020 at 17:53
  • As a note, avoid ifPresent and get if at all possible; they're serious code smells. Instead, GenericType.error(error.orElse(...)). Commented May 12, 2020 at 17:55
  • It's due to the fact that by using SpecificError in your method you can't guarantee anymore to return "anything that extends AbstractError". So if you implement a AnotherSpecificError extends AbstractError, theorically you could have GenericType<AnotherSpecificError> aes = method(); to pass the compilation and explode on runtime due to the SpecificError inferrence Commented May 12, 2020 at 18:02
  • @OlivierDepriester as far as I understand, the following code is valid in Java BaseType b = new ChildType(c); So, specificError can be referred with this parent type (AbstractError) here. I do not see, Type safety issues here. Caller of 'method' needs an instance only with the interface in AbstractError. And also how do you explain that First piece of code is working, it is also working with SpecificError and uses the same underlying function GenericType.error()? I'm not able to follow. Commented May 12, 2020 at 18:44

3 Answers 3

1

From the method method(), you are calling GenericType.shortcut(error), where error is of the type Optional<SpecificError>. shortcut demands a type argument R, which you are trying to fulfill with R = SpecificError. So you are trying to return a GenericType<SpecificError>, but your method signature declares that it returns a GenericType<AbstractError>.

A GenericType<SpecificError> is not a GenericType<AbstractError>, because generics are invariant.

You could fix this by replacing

Optional<SpecificError> error = Optional.of(new SpecificError()); 

with

Optional<AbstractError> error = Optional.of(new SpecificError()); 
Sign up to request clarification or add additional context in comments.

6 Comments

thanks for your explanation. But I'm still curious, why the first piece of code works? I'm doing exactly the same there, which is calling the method GenericMethod.error which has following syntax public static <R extends AbstractError> GenericType<R> error(R value) {} I'm pasting the working code again here ` Optional<SpecificError> error = Optional.of(new SpecificError()); if (error.isPresent()) { ` return GenericType.error(error.get()); }`
The first code works because there is no requirement for the return type and the argument to match.
@MC emperor, I had a method which was retuning SpecificError, so following did not work Optional<AbstractError> error = getMethodWithSpecificError(); I do not know why, but then I changed the syntax of getMethodWithSpecificError to return Optional<AbstractError>, it works, even though the getMethodWithSpecificError still creates an instance of SpecificError. So, i assume, there are some strange rules with Optional subtyping too, they work in some cases and do not work in some
@Nitesh One thing is sure, the rules are very well defined in the JLS. But in order to reproduce this behavior, you should post the exact code along with your question.
it is unfortunately, not possible to share the full code because of privacy concerns. But, i have learnt my lesson that GenericType<Base> and GenericType<Child> are not related, i was under the wrong impression
|
0

As you declare the GenericType as <R extends AbstractError> at shortcut()

It is now forced to return a subtype. That is why the compiler is creating issues.

The concept witnessed here is Type Equality Constraint , covered under Java Generics

Thing should get going if you change the method signature to

public static <R> GenericType<R> shortcut(Optional<R> error) { // remove extends AbstractError 

1 Comment

I can not remove R extends AbstractError, class GenericType works only with types extending AbstractError. And i also do not understand why it works when I do the Optional expansion' in the actual method. GenericType.Error also works with the R extending Abstract error. Here is the signature of error methd public static <R extends AbstractError> Either<R> error(R value) {
0
public GenericType<AbstractError> method{ Optional<SpecificError> error = Optional.of(new SpecificError()); return GenericType.shortcut(error); } public static <R extends AbstractError> GenericType<R> shortcut(Optional<R> error) { 

shortcut needs an Optional parameter whose type parameter matches the return type.

But yours doesn't: SpecificError isn't the same as AbstractError.

All you need is for error to be R or a subclass, so add an upper bound to the parameter type:

Optional<? extends R> error 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.