Firstly apologies about the not so great title, I am new to Java and wasn't sure how to title this.
I have a interface class "TestInterface":
ublic interface TestInterface {
String getForename();
void setForename(String forename);
String getSurname();
void setSurname(String surname);
}
"TestImpl" implements "TestInterface":
public class TestImpl implements TestInterface{
private String forename;
private String surname;
@Override
public String getForename() {
return forename;
}
public void setForename(String forename) {
this.forename = forename;
}
@Override
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
}
Then I have a call called "ExtendTest" which extends "TestImpl":
public class ExtendTest extends TestImpl{
private String firstLineAddress;
public String getFirstLineAddress() {
return firstLineAddress;
}
public void setFirstLineAddress(String firstLineAddress) {
this.firstLineAddress = firstLineAddress;
}
}
I then have this "Entity" class:
import java.util.List;
public class Entity {
private List<TestInterface> testInterfaces;
private List<ExtendTest> extendTests;
public List<TestInterface> getTestInterfaces() {
return testInterfaces;
}
public void setTestInterfaces(List<TestInterface> testInterfaces) {
this.testInterfaces = testInterfaces;
}
public List<ExtendTest> getExtendTests() {
return extendTests;
}
public void setExtendTests(List<ExtendTest> extendTests) {
this.extendTests = extendTests;
}
}
and finally this "DoStuff" class where the dostuff method accepts a parameter of type List
import java.util.List;
public class DoStuff {
public void doStuff(List<TestInterface> testData) {
}
}
I try to test this like so:
public class Main {
public static void main(String[] args) {
System.out.println("Hello, World!");
DoStuff doStuff = new DoStuff();
Entity entity = new Entity();
// Works
doStuff.doStuff(entity.getTestInterfaces());
// Does not work
doStuff.doStuff(entity.getExtendTests());
}
}
However where the comment is "Does not work" their is an error
Required type:
List<TestInterface>
Provided:
List<ExtendTest>
My question is how do I make it so that I can pass it in. My understanding was that becase they all implement TestInterface that it would work but I think I am wrong with this.
Thanks for any help and learnings here :)
CodePudding user response:
You've run afoul of PECS. I recommend reading the linked answer for a more detailed explanation, but here's the bits specific to your use case.
When you have a generic type (List
, in your case), if you only read from it, you should write List<? extends MyInterface>
. If you only write to it, you should write List<? super MyInterface>
. If you do both, then you want List<MyInterface>
. Why do we do this? Well, look at your code.
public void doStuff(List<TestInterface> testData) { ... }
This function takes a List<TestInterface>
. The List
interface has a ton of capability. You can add and remove things to it in addition to just reading from it. And doStuff
expects a list of TestInterface
. So it's entirely fair game for the implementation of doStuff
to do
testData.add(new ClassIJustMadeUp());
assuming ClassIJustMadeUp
implements TestInterface
. So we definitely can't pass this function a List<ExtendTest>
, since that list type can't contain ClassIJustMadeUp
.
However, if your function does only read from the list and isn't planning to add anything to it, you can write the signature as
public void doStuff(List<? extends TestInterface> testData) { ... }
and now you can pass a List
of any type which extends TestInterface
. It's fine to read from this list, since any type which extends TestInterface
clearly can be upcast safely to TestInterface
. But if we try to add a list element, that's a compiler error since the list doesn't necessarily support that particular type.