Given the following example, why am I able to override the return type List<? extends IConfigUser>
as List<ConfigUser>
in getUserList()
but cannot do the same for the parameter of setUserList()
?
Isn't ConfigUser
considered a supertype of IConfigUser
in this case?
public class Test {
public interface IConfigUser {
}
public interface IConfig {
public List<? extends IConfigUser> getUserList();
public void setUserList(List<? extends IConfigUser> list);
}
public class ConfigUser implements IConfigUser {
}
// The type Test.Config must implement the inherited abstract method
// Test.IConfig.setUserList(List<? extends Test.IConfigUser>)
public class Config implements IConfig {
@Override
public List<ConfigUser> getUserList() {
return null;
}
// The method setUserList(List<ConfigUser> list) of type Test.Config
// must override or implement a supertype method
@Override
public void setUserList(List<ConfigUser> list)
{
}
}
}
You can return ("specialize") the return type of
getUserList()
due to covariance, i.e. if you call that method on aIConfig
reference all you know is that you'll get aList<? extends IConfigUser>
and aList<ConfigUser>
is aList<? extends IConfigUser>
so the requirements are satisfied.If you call that on a
Config
reference the information is more concrete but the basic requirements are still met.With
setUserList(...)
the situation is different: it allows you to pass any "subclass" ofList<? extends IConfigUser>
which can be aList<ConfigUser>
but it also can be something else, e.g. aList<SomeConfigUser>
.Btw, since you don't know the concrete generic parameter of
list
insetUserList(List<ConfigUser> list)
the compiler will also only allow you to read from that list, never add to it - for the same reason as above: you don't know what you get and whether adding aConfigUser
is allowed because the list could only allowSomeConfigUser
instances to be added.