3 ms·
This seems to be a set of examples that don't work, rather than a set of examples that prove a point. For example: interface IFoo<T> { Number foo(T t); }
by lowbloodsugar 10y ago
This seems to be a set of examples that don't work, rather than a set of examples that prove a point. For example:
interface IFoo<T> { Number foo(T t); }
class Foo<T extends Number> implements IFoo<T> {
public Number foo(T t) { return t; }
}
Number bar(IFoo<String> foos) { return foos.foo("Nan"); }
This implies that something bad is going on. But the class in the middle is a red herring. It is not used in this example, nor can it be used as an argument to the `bar` method. Try instantiating a Foo<String> and it wont work. Try instantiating a Foo<Integer> and then passing that to bar(), which requires an IFoo<String>. Both are compile time errors.
By all means claim, correctly, that support of unchecked code and raw types means that the type system is not "sound", but the incorrect examples undermine the claim that this is a problem. (I don't personally think that its a problem, and the fact that someone claiming otherwise can only provide examples that fail to prove that its a problem isn't terribly convincing. But then I grew up with 6502 ASM, which had a fairly limited type system).
- munificent 10y agoThis example is not to show that Java is unsound. It's to teach you the difference between types that are invalid and types that would be unsafe but are allowed because you can never instantiate one. Like you note: > Try instantiating a Foo<Integer> and then passing that to bar(), which requires an IFoo<String>. That's what Ross is trying to get you to see. He's saying that Java does not have to make the declaration of IFoo<T> an error because it can rely on the fact that your later instantiation of it with <String> will be an error instead. As long as the bad code gets flagged as an error somehow before it gets used, you're safe. The actual unsoundness bug is the "class Unsound" example in Josh Bloch's tweet. What it shows was that you can't safely rely on "we'll flag an error when you create an instance of this type, so we don't need to flag an error in its declaration." Because you can assign null to be an instance of the type and then you sidestep the later error.
- lowbloodsugar 10y agoI disagree. class Foo<T extends Number> implements IFoo<T> {} Does not implement an unconstrained T. It implements "T extends Number". When the T appears in an implements, its not declaring a new type. You can't do, for example class Foo implements IFoo<T> {} java: cannot find symbol T The mistake is believing that the T's, in the example above, are in any way related. It would be more appropriate to write: interface Foo<TUnconstrained> {} class Foo<TExtendsNumber extends Number> implements IFoo<TExtendsNumber> {...} And then it becomes clear that the foos in bar will never be a Foo, because Foo does not implement IFoo<String> and never will. Yes you can nuke it unchecked code. No, I don't have a problem with that. But the example given is not a useful example. And this example is in response to the previous one where he asks "Is this code safe? .. your gut instinct would say this is unsafe". No. My gut instinct is that it would not compile. And indeed: java: type argument java.lang.String is not within bounds of type-variable T
- munificent 10y ago> I disagree. I don't know what you're disagreeing with. > And then it becomes clear that the foos in bar will never be a Foo, because Foo does not implement IFoo<String> and never will. The statement "Foo does not implement IFoo<String>" is not meaningful. "Foo" is not a type, it's a type constructor. To refer to a type, you need to say Foo of what. And it is the case that Foo<String> implements IFoo<String>. Consider: interface IFoo<TUnconstrained> { Number foo(TUnconstrained t); } void useFoo(IFoo<String> foo) { Number n = foo.foo("not a number"); } This code compiles fine. Add this: class Foo<TExtendsNumber extends Number> implements IFoo<TExtendsNumber> { public Number foo(TExtendsNumber t) { return t; } } It still compiles fine. This is exactly what the author is saying. The reason it is fine for the compiler to permit the above code is because it is relying on the fact that you will not be able to instantiate Foo<String>. This is one half of the puzzle of the actual unsoundness example. The other half is that you can then sidestep that restriction using a wildcard to define a type that hypothetically meets the required bound even though no such real type exists. That would still be safe, since you've never be able to initialize that variable since no value of that type is possible. Except that Java lets you initialize it with null.