简体   繁体   中英

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)

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.

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.

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...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM