Home > Enterprise >  How to pass multiple Types that implement the same interface?
How to pass multiple Types that implement the same interface?

Time:06-13

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.

  • Related