2

I tried to understand next thing. This block code will print next

example.TestGenerics$One@49c2faae
example.TestGenerics$Two@20ad9418

So I suppose that cast was complete successfully, but I expect ClassCastException for second object in list.

public class TestGenerics
{
    public static void main(String[] args)
    {
        Generic<One> integerGeneric = new Generic<One>(new Container(Lists.<Object>newArrayList(new One(), new Two())));
        integerGeneric.checkContainer();
    }

    static class Generic<T>
    {
        private Container container;

        public Generic(Container container)
        {
            this.container = container;
        }

        public void checkContainer()
        {
            for (Object key : container.getObjects())
            {
                final T genericKey = (T)key;
                System.out.println(genericKey);
            }
        }
    }

    static class Container
    {
        Iterable<Object> objects;

        public Container(Iterable<Object> objects)
        {
            this.objects = objects;
        }

        public Iterable<Object> getObjects()
        {
            return objects;
        }
    }

    static class One
    {

    }

    static class Two
    {

    }
}

P.S. Also I faced a problem that generic type cast (using (T)object) returns null instead of throwing exception ClassCastExceptiong, but i cant reproduce it. If anyone know about this, comment here, please

Gcinbax
  • 187
  • 3
  • 12

1 Answers1

-2

I have learned that JVM see generic like Object type in runtime (or nearest type, if it has "exists")

Not necessarily Object. This is the case for unbounded generics, like in your code. If it has a bound, like <T extends ParentClass>, then the runtime type would be ParentClass. Since your code has no bound it will be Object and this wouldn't cause any problem during a cast.

Now, to improve your type checks and to get proper compiler errors, you'll need to add a generic type to Container:

static class Container<T> {
    Iterable<T> objects;

    public Container(Iterable<T> objects) {
        this.objects = objects;
    }

    public Iterable<T> getObjects() {
        return objects;
    }
}

And update Generic to avoid using a raw Container class:

static class Generic<T> {
    private Container<T> container;

    public Generic(Container<T> container) {
        this.container = container;
    }

    public void checkContainer() {
        for (T key : container.getObjects()) {
            final T genericKey = key;
            System.out.println(genericKey);
        }
    }
}

Now add a diamond operator in your main method to:

Generic<One> integerGeneric = new Generic<One>(new Container(Lists.<Object>newArrayList(new One(), new Two())));

And you'll then have:

Generic<One> integerGeneric = new Generic<>(new Container<>(Lists.newArrayList(new One(), new Two())));

And a compiler error stating that Lists.newArrayList(new One(), new Two()) isn't allowed if the desired type is One.


Btw: you still can bypass this compiler check by either using raw types or Object as the generic type and this still won't cause a runtime exception, because both Container and Generic classes have no bounds.

Community
  • 1
  • 1
Tom
  • 16,842
  • 17
  • 45
  • 54
  • If someone wonders about the downvote: [Tim Biegeleisen](https://stackoverflow.com/users/1863229/tim-biegeleisen) used it as one of his targets for his revenge downvotes. – Tom Sep 30 '17 at 09:58