Why and how are these two conditionals treated differently by the compiler?

178 views Asked by At

The following two code samples represent the same logic. Check to see if a string is null and branch based upon that check. The first sample compiles safely. The second produces a type mismatch error related to Java generics. My question seems simple enough, but it eludes me. Why does the compiler treat these two statements differently? How can I better understand what's going on here?

/* compiles cleanly */
protected Collection<String> getUserRoles(Object context,
        Set<String> mappableRoles) {
    String cookieValue = extractCookieValue(context);
    if (cookieValue != null) {
        return securityService.getRolesForUser(cookieValue);
    } else {
        return Collections.emptySet();
    }
}


/* produces a compiler error */
protected Collection<String> getUserRoles(Object context,
            Set<String> mappableRoles) {
    String cookieValue = extractCookieValue(context);
    return cookieValue == null ? Collections.emptySet()
            : securityService.getRolesForUser(cookieValue);
}

The compiler error from Eclipse.

Type mismatch: cannot convert from Set<capture#1-of ? extends Object> to Collection<String>

As requested, here's the relevant portion of the SecurityService interface.

public interface SecurityService {
    public Set<String> getRolesForUser(String userId);
}
3

There are 3 answers

1
Marsellus Wallace On BEST ANSWER

The problem should reside in how the compiler interprets the return value of the ternary operator. You might want to take a look at part 15.25 of the JLS or at this question (kinda related because it's even further complicated by autoboxing and it throws an error at runtime instead of at compile time).

Hope this puts you on the right direction.

2
mike9322 On

It's because Collections.emptySet() returns an untyped Set. Instead, try this:

Collections.<String>emptySet()
0
e-zinc On

Collections.emptySet() is declared as

public static final <T> Set<T> emptySet()

The first T is used for Type Inference. The second getUserRoles implementation is too complex for java compiler to detect right type. That is the reason of the issue. Work around:

protected Collection<String> getUserRoles(Object context,
            Set<String> mappableRoles) {
    String cookieValue = extractCookieValue(context);
    Collection<String> a = null;
    return cookieValue == null ? a = Collections.emptySet()
            : securityService.getRolesForUser(cookieValue);
}