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 a IConfig
reference all you know is that you'll get a List<? extends IConfigUser>
and a List<ConfigUser>
is a List<? 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" of List<? extends IConfigUser>
which can be a List<ConfigUser>
but it also can be something else, e.g. a List<SomeConfigUser>
.
Btw, since you don't know the concrete generic parameter of list
in setUserList(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 a ConfigUser
is allowed because the list could only allow SomeConfigUser
instances to be added.