4 ms·
> Pattern Matching for instanceof Why is the cast even necessary? Isn't the cast only part of the type checker and thus unnecessary with a smarter type checker
by wereHamster 7y ago
> Pattern Matching for instanceof
Why is the cast even necessary? Isn't the cast only part of the type checker and thus unnecessary with a smarter type checker? For example TypeScript can do it, if you have code following an if condition that checks the type of the variable, you can use that said variable as if it were of that type without any further assertions necessary, eg.
let x: unknown;
if (typeof x === "string") {
console.log(x.charCodeAt(0));
}
- aardvark179 7y agoIt starts to get more complex than you might think. Suppose I declare x to be of interface type A and then pattern match it to a disjoint interface type B. Now unless I get depresse that with a cast or a new variable name then I need to express it as some kind of intersection type and this may need to extend into the bytecode and vm. It might even be an intersection between an object and an interface type, and for historical reasons those have different bytecodes for invoking methods! Flow typing is really cool, but it can make things way more complex.
- oaiey 7y agoThis kind of flow analytics are pretty new IMHO. C# supports these e.g. for nullable checks. But I think Java and C# are designed a bit too early for having these kind of flexibility with the variable type. Also consider that changing the type also has other consequences like invoking other virtual methods (e.g. C# new operator on methods) which can be confusing because a type change above is not explicit. PS: just came to my mind: that is a breaking change (in C#) due to the new operator exanple. Will never come (for C#). Maybe Java does not have this issue but most likely they have a similar problem This is an awesome feature for typescript. Love their work there.
- frant-hartm 7y agoTwo reasons - current convention and backwards compatibility. Java developers are simply used to the cast, it is commonly occurring pattern (even if it is from necessity). The backwards compatibility part: class Foo { void foo(Object o) { } } class Bar extends Foo { void foo(Integer i) { } } ... Foo x = ...; Integer i = 42; if (x instanceof Bar) { x.foo(i); } Currently this calls Foo.foo, if there was a smart cast it would call Bar.foo, possibly breaking existing code. EDIT: Added method parameter.
- koreth1 7y agoThat's not correct. Java method dispatch is always dynamic, so x.foo() will always call the foo() method on whatever concrete type x actually is. The declared type of the variable that holds the reference to that object doesn't matter, nor does casting (with a couple exceptions, none of which apply here). But don't trust me, try it! | Welcome to JShell -- Version 11.0.5 | For an introduction type: /help intro jshell> class Foo { void foo() { System.out.println("Super"); } } | created class Foo jshell> class Bar extends Foo { void foo() { System.out.println("Sub"); } } | created class Bar jshell> Foo f = new Bar(); f ==> Bar@58651fd0 jshell> Bar b = new Bar(); b ==> Bar@5419f379 jshell> f.foo(); Sub jshell> b.foo(); Sub jshell> ((Foo)b).foo(); Sub
- frant-hartm 7y agoThat's right, I forgot a method parameter: jshell> class Foo { void foo(Object o) { System.out.println("Super"); } } | created class Foo jshell> class Bar extends Foo { void foo(Integer i) { System.out.println("Sub"); } } | created class Bar jshell> Foo f = new Bar(); f ==> Bar@7f9a81e8 jshell> Integer i = 1; i ==> 1 jshell> f.foo(i) Super jshell> ((Bar)f).foo(i); Sub
- burakemir 7y agoYour statement is wrong, in the following sense: at runtime, the call to x.foo() would always start look at the runtime type (which in this case is Bar, or some subclass of it) - otherwise overriding methods would not work. https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12.4.4 https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.htm...
- frant-hartm 7y agoYes, I forgot a method parameter there.