Why can't the Java compiler figure this out? - java

Why is the compiler unable to infer the correct type for the result from Collections.emptySet() in the following example?
import java.util.*;
import java.io.*;
public class Test {
public interface Option<A> {
public <B> B option(B b, F<A,B> f);
}
public interface F<A,B> {
public B f(A a);
}
public Collection<String> getColl() {
Option<Integer> iopt = null;
return iopt.option(Collections.emptySet(), new F<Integer, Collection<String>>() {
public Collection<String> f(Integer i) {
return Collections.singleton(i.toString());
}
});
}
}
Here's the compiler error message:
knuttycombe#knuttycombe-ubuntu:~/tmp/java$ javac Test.java
Test.java:16: <B>option(B,Test.F<java.lang.Integer,B>) in
Test.Option<java.lang.Integer> cannot be applied to (java.util.Set<java.lang.Object>,
<anonymous Test.F<java.lang.Integer,java.util.Collection<java.lang.String>>>)
return iopt.option(Collections.emptySet(), new F<Integer, Collection<String>>() {
^
1 error
Now, the following implementation of getColl() works, of course:
public Collection<String> getColl() {
Option<Integer> iopt = null;
Collection<String> empty = Collections.emptySet();
return iopt.option(empty, new F<Integer, Collection<String>>() {
public Collection<String> f(Integer i) {
return Collections.singleton(i.toString());
}
});
}
and the whole intent of the typesafe methods on Collections is to avoid this sort of issue with the singleton collections (as opposed to using the static variables.) So is the compiler simply unable to perform inference across multiple levels of generics? What's going on?

Java needs a lot of hand holding with its inference. The type system could infer better in a lot of cases but in your case the following will work:
print("Collections.<String>emptySet();");

First you can narrow down your problem to this code:
public class Test {
public void option(Collection<String> b) {
}
public void getColl() {
option(Collections.emptySet());
}
}
This does not work, you need a temporary variable or else the compiler cannot infer the type. Here is a good explanation of this problem: Why do temporary variables matter in case of invocation of generic methods?

Collections.emptySet() is not a Collection<String> unless Java knows that it needs a Collection<String> there. In this case, it appears that the compiler is being somewhat silly about the order that it tries to determine types, and tries to determine the return type of Collections.emptySet() before trying to determine the intended template parameter type for B is actually String.
The solution is to explictly state that you need Collections.<String>emptySet(), as mentioned by GaryF.

It looks like a typecasting issue - i.e., that it's being required to cast Object (in Set<Object>, which would be the type of the empty set) to String. Downcasts are not, in the general case, safe.

Related

Generic method to return function returning generic self-type without the need to cast

Honestly, I'm not even sure whether that title makes sense. Hopefully the code following will explain the issue at hand.
package what.ever.you.like;
import java.util.function.UnaryOperator;
class SelfTypeTemplates {
public static <SELF extends AbstractSelfType> UnaryOperator<SELF> simpleBound() {
return self -> self;
}
public static <SELF extends AbstractSelfType<SELF>> UnaryOperator<SELF> boundWithGenericType() {
return self -> self;
}
}
class ConcreteSelfType extends AbstractSelfType<ConcreteSelfType> {
public ConcreteSelfType() {
super(ConcreteSelfType.class);
}
public ConcreteSelfType applySimpleBound() {
// How to get rid of the type cast?
return (ConcreteSelfType) SelfTypeTemplates.simpleBound().apply(this);
}
public ConcreteSelfType applyBoundWithGenericType() {
// Compile error because `this` is ConcreteSelfType, but required is SELF
return SelfTypeTemplates.boundWithGenericType().apply(this);
}
}
class AbstractSelfType<SELF extends AbstractSelfType<SELF>> {
protected final SELF myself;
protected AbstractSelfType(final Class<?> type) {
this.myself = (SELF) type.cast(this);
}
}
My issue is with the two methods applySimpleBound() and applyBoundWithGenericType().
The former is compiling fine, but needs explicit casting, which is what I'd like to get rid of.
The later does not compile, because .apply(this) requires a type SELF but provided is ConcreteSelfType.
So my question is, how do I specify the signature of a method in SelfTypeTemplates to return an UnaryOperator<SELF> so that invoking the returned function (.apply(this)), does not need casting in the client code (i.e. ContreteSelfType)?
Tried to play with different bounds in the generic and return type. Haven't found a working version without type casting.
Sometimes the compiler cannot infer the correct type for what ever reason. To work around this issue you can specify it like this:
class ConcreteSelfType extends AbstractSelfType<ConcreteSelfType> {
public ConcreteSelfType() {
super(ConcreteSelfType.class);
}
public ConcreteSelfType applySimpleBound() {
// How to get rid of the type cast?
return SelfTypeTemplates.<ConcreteSelfType>simpleBound().apply(this);
}
public ConcreteSelfType applyBoundWithGenericType() {
// Compile error because `this` is ConcreteSelfType, but required is SELF
return SelfTypeTemplates.<ConcreteSelfType>boundWithGenericType().apply(this);
}
}
Both options compile this way and you don't need a cast.

Is there some sort of generic bound in Java?

I am defining a type Option<T> in Java that should behave as much as possible as Rust's equivalent.
It has a method, Option::flatten, that is only implemented if the inner T is some other Option<T>. I am thinking of something like this:
public class Option<T> {
/* fields, constructors, other methods */
#Bound(T=Option<U>)
public <U> Option<U> flatten() {
if (isNone()) return None();
else return this.unwrap();
}
}
But the syntax is of course completely fictional. Is there some way to make this work in Java? I know static methods are an option, but they can't be called like a normal method which is the only goal of this type.
This is not supposed to be a standalone thing, but rather a part of a larger Java implementation of Rust iterators I'm currently working on.
The problem with trying to come up with a non-static method such as flatten is that in Java one cannot conditionally add more methods to a class based on whether the type parameter of the class fulfills a certain constraint.
You can, however, make it a static method and constrain its arguments to whatever you need.
class Option<T> {
// ...
public static <U> Option<U> flatten(Option<Option<U>> option) {
if (option.isNone()) return None();
return option.unwrap();
}
}
Which would work for valid implementations of None, isNone and unwrap.
A more complete example follows.
public static class Option<T> {
private final T value;
private Option(T x) {
this.value = x;
}
public static <T> Option<T> of(T x) {
java.util.Objects.requireNonNull(x);
return new Option<>(x);
}
public static <T> Option<T> None() {
return new Option<>(null);
}
public T unwrap() {
java.util.Objects.requireNonNull(this.value);
return this.value;
}
public boolean isNone() {
return this.value == null;
}
public static <U> Option<U> flatten(Option<Option<U>> option) {
if (option.isNone()) return Option.None();
return option.unwrap();
}
#Override
public String toString() {
if (this.isNone()) {
return "None";
}
return "Some(" + this.value.toString() + ")";
}
}
Usage:
var myOption = Option.of(Option.of(5));
System.out.println("Option: " + myOption);
System.out.println("Flattened: " + Option.flatten(myOption));
Output:
Option: Some(Some(5))
Flattened: Some(5)
I think the way you want to handle this is not to actually have a flatten() method, but have different handling in your constructor. Upon being created, the constructor should check the type it was handed. If that type is Option, it should try and unwrap that option, and set its internal value to the same as the option it was handed.
Otherwise, there isn't really a way for an object to 'flatten' itself, because it would have to change the type it was bounded over in the base case. You could return a new object from a static method, but are otherwise stuck.
I want to point out some of the potential headaches and issues regarding this re-implementation of Optional<T>.
Here's how I would initially go about it:
public class Option<T> {
/* fields, constructors, other methods */
public <U> Option<U> flatten() {
if (isNone()) return None();
T unwrapped = this.unwrap();
if (unwrapped instanceof Option) {
return (Option<U>) unwrapped; //No type safety!
} else {
return (Option<U>) this;
}
}
}
However, this code is EVIL. Note the signature of <U> Option<U> flatten() means that the U is going to be type-inferenced into whatever it needs to be, not whatever a potential nested type is. So now, this is allowed:
Option<Option<Integer>> opt = /* some opt */;
Option<String> bad = opt.flatten();
Option<Option<?>> worse = opt.<Option<?>>flatten();
You will face a CCE upon using this for the other operations, but it allows a type of failure which I would say is dangerous at best. Note that any Optional<Optional<T>> can have #flatMap unwrap for you: someOpt.flatMap(Function.identity());, however this again begs the question of what caused you to arrive at a wrapped optional to begin with.
Another answer (by #NathanielFord) notes the constructor as an option, which seems viable as well, but will still face the runtime check upon construction (with it simply being moved to the constructor):
public class Option<T> {
/* fields, constructors, other methods */
public Option<T>(T someValue) { ... }
public Option<T>(Option<T> wrapped) {
this(wrapped.isNone() ? EMPTY_OBJECT : wrapped.unwrap());
}
public Option<T> flatten() {
return this; //we're always flattened!
}
}
Note as well, the re-creation of Optional<T> by
#E_net4thecommentflagger has the potential for a nasty future bug: Optional.ofNullable(null).isNone() would return true! This may not be what you want for some potential use-cases, and should #equals be implemented in a similar manner, you'd end up with Optional.ofNullable(null).equals(Optional.None()), which seems very counter-intuitive.
All of this to say, that while Rust may require you to deal with these nested optionals, you are writing code for Java, and many of the potential restrictions you faced before have changed.

Mockito and Hamcrest: how to verify invocation of Collection argument?

I'm running into a generics problem with Mockito and Hamcrest.
Please assume the following interface:
public interface Service {
void perform(Collection<String> elements);
}
And the following test snippet:
Service service = mock(Service.class);
// ... perform business logic
verify(service).perform(Matchers.argThat(contains("a", "b")));
So I want to verify that my business logic actually called the service with a collection that contains "a" and "b" in that order.
However, the return type of contains(...) is Matcher<Iterable<? extends E>>, so Matchers.argThat(...) returns Iterable<String> in my case, which naturally does not apply to the required Collection<String>.
I know that I could use an argument captor as proposed in Hamcrest hasItem and Mockito verify inconsistency, but I would very much like not to.
Any suggestions!
Thanks!
You can just write
verify(service).perform((Collection<String>) Matchers.argThat(contains("a", "b")));
From the compiler's point of view, this is casting an Iterable<String> to a Collection<String> which is fine, because the latter is a subtype of the former. At run time, argThat will return null, so that can be passed to perform without a ClassCastException. The important point about it is that the matcher gets onto Mockito's internal structure of arguments for verification, which is what argThat does.
As an alternative one could change the approach to ArgumentCaptor:
#SuppressWarnings("unchecked") // needed because of `List<String>.class` is not a thing
// suppression can be worked around by using #Captor on a field
ArgumentCaptor<List<String>> captor = ArgumentCaptor.forClass(List.class);
verify(service).perform(captor.capture());
assertThat(captor.getValue(), contains("a", "b"));
Notice, that as a side effect this decouples the verification from the Hamcrest library, and allows you to use any other library (e.g. Truth):
assertThat(captor.getValue()).containsExactly("a", "b");
If you get stuck in situations like these, remember that you can write a very small reusable adapter.
verify(service).perform(argThat(isACollectionThat(contains("foo", "bar"))));
private static <T> Matcher<Collection<T>> isACollectionThat(
final Matcher<Iterable<? extends T>> matcher) {
return new BaseMatcher<Collection<T>>() {
#Override public boolean matches(Object item) {
return matcher.matches(item);
}
#Override public void describeTo(Description description) {
matcher.describeTo(description);
}
};
}
Note that David's solution above, with casting, is the shortest right answer.
You can put your own lambda as an ArgumentMatcher
when(myClass.myMethod(argThat(arg -> arg.containsAll(asList(1,2))))
.thenReturn(...);
Why not just verify with the expected arguments, assuming the list only contains the two items, e.g.:
final List<String> expected = Lists.newArrayList("a", "b");
verify(service).perform(expected);
Whilst I agree with Eugen in principle, I think that relying on equals for String comparison is acceptable... besides, the contains matcher uses equals for comparison anyway.
Similar to another answer here you can do the following:
verify(yourmock, times(1)).yourmethod(argThat(arg -> arg.containsAll(asList("a", "b"))));
You could have your own java.util.Collection implementation and override the equals method like below.
public interface Service {
void perform(Collection<String> elements);
}
#Test
public void testName() throws Exception {
Service service = mock(Service.class);
service.perform(new HashSet<String>(Arrays.asList("a","b")));
Mockito.verify(service).perform(Matchers.eq(new CollectionVerifier<String>(Arrays.asList("a","b"))));
}
public class CollectionVerifier<E> extends ArrayList<E> {
public CollectionVerifier() {
}
public CollectionVerifier(final Collection<? extends E> c) {
super(c);
}
#Override
public boolean equals(final Object o) {
if (o instanceof Collection<?>) {
Collection<?> other = (Collection<?>) o;
return this.size() == other.size() && this.containsAll(other);
}
return false;
}
}

Java Generics Question

Alright, I thought I understood generics pretty well, but for some reason I can't get my head wrapped around why this doesn't work. I have two classes, or I should say that Google has two classes (I'm trying to implement their Contacts API). They have a ContactEntry class (abbreviated below):
package com.google.gdata.data.contacts;
public class ContactEntry extends BasePersonEntry<ContactEntry> {
public ContactEntry() {
super();
getCategories().add(CATEGORY);
}
public ContactEntry(BaseEntry<?> sourceEntry) {
super(sourceEntry);
}
}
I left off one or two methods, but nothing important, its really just an implementation of its parent class BasePersonEntry which has most of the important stuff that concerns a person entry abbreviated code below:
package com.google.gdata.data.contacts;
public abstract class BasePersonEntry<E extends BasePersonEntry> extends
BaseEntry<E> {
public BasePersonEntry() {
super();
}
public BasePersonEntry(BaseEntry<?> sourceEntry) {
super(sourceEntry);
}
public List<CalendarLink> getCalendarLinks() {
return getRepeatingExtension(CalendarLink.class);
}
public void addCalendarLink(CalendarLink calendarLink) {
getCalendarLinks().add(calendarLink);
}
public boolean hasCalendarLinks() {
return hasRepeatingExtension(CalendarLink.class);
}
}
Now... what I can't quite understand is if I do something like the following:
public void method1(StringBuilder sb, ContactEntry contact) {
if (contact.hasCalendarLinks()) {
for (CalendarLink calendarLink : contact.getCalendarLinks()) {
...
}
}
}
Everything works fine. It is able to interpret that getCalendarLinks returns a list of type CalendarLink. However, if I want to abstract this method and have my method use BasePersonEntry, like the following:
public void method1(StringBuilder sb, BasePersonEntry entry) {
if (entry.hasCalendarLinks()) {
for (CalendarLink calendarLink : entry.getCalendarLinks()) {
...
}
}
}
I get a compiler error:
incompatible types
found : java.lang.Object
required: com.google.gdata.data.contacts.CalendarLink
For the life of me I just can't understand why? The call to getCalendarLinks is the EXACT same method (via inheritance), its returning the EXACT same thing. Maybe it has to do with BasePersonEntry being an abstract class?
If anyone, can shed some light on this I would be very much obliged. If it helps you can find a full version of this source code hosted by Google here: Link To Google Library Download. I was attempting this with version 1.41.3 of their gdata-java libraries.
The problem with your new definition, is that it's using Raw type not Generic type.
As a result type is erased from everything, including getCalendarLinks and its signature is reduced to equivalent of List<Object> getCalendarLinks( )
To fix it, change declaration to:
public void method1(StringBuilder sb, BasePersonEntry<?> entry)
Note <?> after BasePersonEntry. This way it's generic type.
Also, you probably want to change the class generic declaration to
public abstract class BasePersonEntry<E extends BasePersonEntry<E> >
Without <E> your compiler ( or IDE ) will generate an unchecked warning.

Java Generics: casting to ? (or a way to use an arbitrary Foo<?>)

So, I have some code that looks approximately like (truncated for brevity - ignore things like the public member variables):
public class GenericThingy<T> {
private T mValue;
public final T[] mCandidates;
public GenericThingy(T[] pCandidates, T pInitValue) {
mCandidates = pCandidates;
mValue = pInitValue;
}
public void setValue(T pNewValue) {
mValue = pNewValue;
}
}
public class GenericThingyWidget {
private final GenericThingy<?> mThingy;
private final JComboBox mBox;
public GenericThingyWidget (GenericThingy<?> pThingy) {
mThingy = pThingy;
mBox = new JComboBox(pThingy.mCandidates);
//do stuff here that makes the box show up
}
//this gets called by an external event
public void applySelectedValue () {
mThingy.setValue(mBox.getSelectedItem());
}
}
}
My problem is that the mThingy.setValue(mBox.getSelectedItem()); call generates the following error:
The method setValue(capture#4-of ?) in the type Generics.GenericThingy<capture#4-of ?> is not applicable for the arguments (Object)
I can get around this by removing the <?> from the declaration of mThingy and pThingy in GenericThingyWidget - which gives me a "GenericThingy is a raw type. References to GenericThingy should be parameterized" warning.
I also tried replacing the setValue call with
mThingy.setValue(mThingy.mCandidates[mBox.getSelectedIndex()]);
which I genuinely expected to work, but that produced a very similar error:
The method setValue(capture#4-of ?) in the type Generics.GenericThingy<capture#4-of ?> is not applicable for the arguments (capture#5-of ?)
Is there any way to do this without generating "raw type" warnings ("unchecked cast" warnings I'm OK with) and without making GenericThingyWidget into a generic type? I'd think I could cast the return of mBox.getSelectedItem() to something, but I can't figure out what that would be.
As a bonus question, why does the replacement call to mThingy.setValue not work?
You lack information in GenericThingyWidget.
The ? you put means : any class extending object. Which means any, not some particular one but I don't know which one. Java can't relate one ? to another, they can not be related one to the other in a class hierarchy tree. So
mThingy.setValue(mThingy.mCandidates[mBox.getSelectedIndex()]);
this tries to put an object of any class in the setValue, which is waiting for any other class, but the ? can not tell Java these two any should be the same class.
Without parameterizing GenericThingyWidget, I don't see any way to work around it.
What I would do : parameterize GenericThingyWidget, and create a Factory static parameterized method :
public static <T> GenericThingyWidget<T> make(T someObject){
...
}
I see two possibilities.
With a private addition to GenericThingyWidget— Goetz's capture helper pattern:
public void applySelectedValue() {
helper(mThingy, mBox.getSelectedIndex());
}
private static <T> void helper(GenericThingy<T> pThingy, int pIndex) {
pThingy.setValue(pThingy.mCandidates[pIndex]);
}
Or, quick-and-dirty, with a modification to the API of GenericThingy:
public void setValue(int value) {
mValue = mCandidates[value];
}
As a bonus question, why does the replacement call to mThingy.setValue not work?
The article by Brian Goetz probably explains this better than I will, but I'll give it a try.
mThingy.setValue(mThingy.mCandidates[mBox.getSelectedIndex()]);
The compiler knows that mThingy has some type parameter, but it doesn't know what the that type is, because it is a wildcard. It creates a placeholder for this type—"capture#4-of ?". The compiler also knows that mCandidates has some type, but it doesn't know what it is either. It creates brand new "capture" type—"capture#5-of ?" While you and I can reason that these should be the same type, the compiler (at least for now) can't jump to that conclusion. Thus, you get the error message.
The capture helper gets around that. Although the compiler doesn't know what the type is, it knows it has a type, so it allows you to pass it to the helper method. Once inside the helper method, there are no wildcards, and the compiler doesn't have to do any reasoning about whether the wildcards really refer to the same type.
Update
OK, try this:
public class GenericThingy<T> {
private Class<T> mClazz;
private T mValue;
public final T[] mCandidates;
public GenericThingy(Class<T> clazz, T[] pCandidates, T pInitValue) {
mClazz = clazz;
mCandidates = pCandidates;
mValue = pInitValue;
}
public void setValue(Object newValue) throws ClassCastException {
mValue = mClazz.cast(newValue);
}
}
What you need to to is parameterize GenericThingyWidget like so:
public class GenericThingyWidget<T> {
private final GenericThingy<? super T> mThingy;
private final JComboBox mBox;
public GenericThingyWidget (GenericThingy<? super T> pThingy) {
mThingy = pThingy;
mBox = new JComboBox(pThingy.mCandidates);
//do stuff here that makes the box show up
}
//this gets called by an external event
public void applySelectedValue () {
mThingy.setValue((T) mBox.getSelectedItem());
}
}
}
Technically, you don't need the ? super T for your example, and would be fine with just a T, and perhaps it would be better in real code if you ever want to get from the GenericThingy instead of just inserting into it.
As KLE said, You can just de-parameterize GenericThingy (replace all the T's with objects). In fact, I think you have to unless you plan to pass the class of T to the constructor of GenericThingyWidget, and then dynamically cast from your mbox.getSelectedItem(), since as far as I can tell, getSelectedItem() only returns Object.

Categories

Resources