Kaos
Kaos

Reputation: 41

Type safety with generics

I have a bunch of simple interfaces:

interface County extends Line{}

interface Country<C extends Line> extends LineContainer<C> {}

interface Line {}

interface LineContainer <L extends Line> {
    public List<L> getLines();
}

And a Service Method

public static <L extends Line,C extends LineContainer<L>> C getContainer( Class<C> containerType, Class<L> lineType ){
    ...somthing...

Calling the Service Method

Country<County> c = getContainer( Country.class, County.class );

faces no error, but the checker says:

Type safety: The expression of type Country needs unchecked conversion to conform to Country

I don't understand that: By calling the service method with County as the L-LineType and C is the Container for L and C is given by Country as the C-Type, thus, I expected type inference would conclude, that a Country object will be served.

Can anyone explain, why I am wrong and if and how I can achieve what I want to?

Background: The idea is - as a user of the service - i can freely combine containers and lines just as needed (as long as the service provider can serve these)

Upvotes: 4

Views: 373

Answers (3)

Kaos
Kaos

Reputation: 41

I think I solved it...

The main mistake in my question was, that I wanted to define the line elements in the container. Now I define in Line to what Header it belongs.

interface Line<R> {}

Than, I define a LineContainer, that serves Lines from the defined Type

interface LineContainer<H, L extends Line<H>> {
    public List<L> getLines();
}

Now I can define a generic service method (looks slightly different to my approach above):

public static <H,L extends Line<H>,C extends LineContainer<H,L>> C getContainer( Class<C> containerType, Class<L> lineType ){
   // ...something...
}

Defining Country and County like this:

interface County extends Line<Country>{}
interface Country extends LineContainer<Country,County>{};

I can now use without any problems:

LineContainer<Country,County> container = getContainer( Country.class, County.class );

Another Example with LineContainer and Line:

interface Note extends Line<Gamut>{}
interface Gamut extends LineContainer<Gamut,Note>{};
LineContainer<Gamut,Note> container = getContainer( Gamut.class, Note.class );

The Service Method has to check, if the required types can be served, but the user of the service cannot combine types that are not comaptible:

NOT ALLOWED

LineContainer<Gamut,County> container = getContainer( Gamut.class, County.class );

as County is not a "Line" of Gamut...

Upvotes: 0

Jai
Jai

Reputation: 8363

This is because the compiler isn't sure that Country.class matches the signature Country<County>. Country.class is considered raw type.

If you write this:

public static <L extends Line, C extends LineContainer<L>> C getContainer(C container, Class<L> lineType) {
    return null;
}

and:

Country<County> c = getContainer(new Country<County>() {
    @Override
    public List<County> getLines() {
        return null;
    }
}, County.class);

Obviously this works.

Now imagine I split the same code into another way:

    Country foo = new Country<County>() {
        @Override
        public List<County> getLines() {
            return null;
        }
    };
    Country<County> c = getContainer(foo, County.class);

This will again give warning at compile time, because of raw type.

Upvotes: 1

Levent Dag
Levent Dag

Reputation: 170

The Problem with your code is that Country can have a Generic Type C which extends Line, however using your method.

getContainer( Country.class, County.class );

This method doesn't tell you that this Country.class has the C type of County.. therefore in theory your return C would be Country.

Unfortunately there is no real fix for this other than to either suppress the warning. Or you don't use a parameter in Country and make C fixed to County.

public interface Country extends LineContainer<County>

You also could use actual objects or suppliers etc.

Upvotes: 0

Related Questions