1

Say I have following sample application, I'm using java 8.

I have following class that has a child class:

public class GrantedAuthority { }
public class GrantedAuthoritySubClass extends GrantedAuthority { }

Following class is where the tricky part happens:

public class UserDetails {

    Collection<? extends GrantedAuthority> authorities;

    public Collection<? extends GrantedAuthority> getAuthorities(){
        return authorities;
    }
}

My main method:

public class Main {

    public static void main(String[] args) {

        UserDetails u = new UserDetails();
        List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
        list.add(new GrantedAuthoritySubClass());
        u.getAuthorities().addAll(list);
    }
}

When I run this I get the following error:

Error:(12, 35) java: incompatible types: java.util.List<GrantedAuthority> cannot be converted to java.util.Collection<? extends capture#1 of ? extends GrantedAuthority>

My problem is, how I can pass a collection of GrantedAuthoritySubClass Objects to the collection returned by u.getAuthorities(). I can't figure it out and there are so many compilation errors attached to various ways I've tried.

Kevin Cruijssen
  • 9,153
  • 9
  • 61
  • 135
nilan59
  • 1,006
  • 9
  • 24
  • 3
    what the point of having wildcard in your example ? take a look at http://stackoverflow.com/questions/2745265/is-listdog-a-subclass-of-listanimal-why-arent-javas-generics-implicitly-p?rq=1 – JEY Apr 26 '16 at 11:24
  • @JEY This is about spring-security 4.0.3 and UserDetails class in spring structured like above. I just wanted to make the question simpler without mentioning that. – nilan59 Apr 26 '16 at 11:27
  • just return Collection i don't see the point of having a wildcard. – JEY Apr 26 '16 at 11:28
  • @JEY Can you please assume that, wild card is there and its not changeable. What should be the solution then. – nilan59 Apr 26 '16 at 11:32
  • Ok it's part of the clientdetails interfaces. My bad. I'm affraid that if you want to add authority you have to create your own method in you UserDetails implementation. – JEY Apr 26 '16 at 11:37
  • check my answer it should fix your problem – JEY Apr 26 '16 at 13:14

4 Answers4

2

According to the PECS rule, your authorities collection is a producer of elements (since it uses extends). This means that the collection can only "produce" or "output" GrantedAuthority objects or its subclasses. It also means you can't put anything into the collection since you don't know the collection's actual type.

If you used super you could put your objects there (but you couldn't get them out), but I suspect that the wildcard is completely unnecessary.

Community
  • 1
  • 1
Kayaman
  • 72,141
  • 5
  • 83
  • 121
2

You can't compile cause you don't know what kind of Collection you get.

You need to change the signature to

Collection<? super GrantedAuthority> getAuthorities()

This illustrates the problem (c) Andrey Tyukin: enter image description here

Community
  • 1
  • 1
Chriss
  • 5,157
  • 7
  • 41
  • 75
1

You try to be too exact using the wildcard (and get yourself into generics-hell that way.)

Basically, try to avoid wildcards and use natural inheritance when possible. In your example, alter your UserDetails as follows:

public class UserDetails {
    Collection<GrantedAuthority> authorities;

    public Collection<GrantedAuthority> getAuthorities(){
        return authorities;
    }
}

... and all will be fine. Especially, you can add an instance of GrantedAuthoritySubClass, as natural inheritance kicks in.

mtj
  • 3,381
  • 19
  • 30
1

Because UserDetails is an interface for spring security you can't change the method definition. You can't add GrantedAuthority with your method. However in the implementation you can override the getAuthorities to return a collection that match the interface definition:

public class UserDetailsImpl implements UserDetails {

    private Collection<GrantedAuthority> authorities = new  ArrayList<>();

    @Override
    public Collection<GrantedAuthority> getAuthorities() {
        return authorities;
    }
}

And now you can use it like this:

UserDetailsImpl u = new UserDetailsImpl();
u.getAuthorities().add(new GrantedAuthority());

It's also possible to use your own GrantedAuthority type like this:

public class MyGrantedAuthority extends GrantedAuthority{

}

public class UserDetailsImpl implements UserDetails {

    private Collection<MyGrantedAuthority> authorities = new  ArrayList<>();

    @Override
    public Collection<MyGrantedAuthority> getAuthorities() {
        return authorities;
    }
}


UserDetailsImpl u = new UserDetailsImpl();
u.getAuthorities().add(new MyGrantedAuthority());
JEY
  • 6,973
  • 1
  • 36
  • 51