java8 default methods can break backward compatibility

In my HSBC interview a London interviewer (Wais) challenged me that he thought default methods are designed for backward compatibility. I now think he was wrong.

The rare cases of incompatibility is an obscure (almost academic) concern. More important are the rules of method resolution when default methods are among the candidates. This topic is similar in spirit to popular interview questions around overriding vs overloading.

[[mastering lambda]] published by Oracle, has a few concise pages on these rules. Then it touches on the imperfections of these rules i.e. the incompatibilities. Its first example is backward incompatibility. If you have legacy java7 class and you compile it in java8, against an updated interface employing default methods, compilation fails. Simplest example:

Legacy ClassA2 has a private method hello(). The new default method added to the InterfaceA1 is public hello(){….}. Compilation in java8 would fail.

3rd example is a serious backward incompatibility issue but I am not convinced it is technically possible. Any legacy code relying on putIfAbsent() must have an implementation of putIfAbsent() somewhere in the codebase. That implementation must be in some class. Due to “class-wins-over-interface” rule, the new default method will never be chosen.

Consider a future-proof java 7 programmer puts putIfAbsent() in a class that’s not really compiled/linked at the time of writing. She thought when this becomes switched on it would be a thread-safe implementation. Then without her knowing the default method actually turns it on. I feel this scenario is far-fetched.

Advertisements

[17]java earliest MS+HSBC IV of 2017

  • Q: When is JIT compiled code performance higher than c++? See separate blog
  • Q: difference between JVM stack vs native stack?
  • Q: ThreadLocal internal implementation?
  • Q: data structures with concurrent modification notifications — how is it implemented?
  • — IPC between processes (language-neutral) —
  • Q: how is shared memory managed?
  • Q: messaging uses sockets and has high overhead. What other solutions can maintain FIFO?
  • %%A: nothing new. The earliest MOM has dealt with this problem long ago. Perhaps multiple files with single producer and single consumer would be ideal. The 2 processes need to operate on both ends of the file. (There could be some kernel support for this.) See https://coderanch.com/t/278842/java/Reading-writing-concurrent-threads-file
  • Q: what kind of jdk locks have you used?
  • %%A: readwrite lock, reentrant lock
  • Q: How would you size your thread pool, based on processor count?
  • Q: For a market data gateway, when would additional threads help (and when would they be useless or counterproductive)?
  • %%A: I/O bound, the processors could be 99% idle. More threads would increase the utilization rate. Ideal is simultaneous saturation.
  • Q: thread cancellation without using Futrues?
  • Q: default methods in interface — is it a breaking change? http://stackoverflow.com/questions/22618493/does-introducing-a-default-method-to-an-interface-really-preserve-back-compatibi has a concise answer
  • Q: how is lambda implemented in java 8? See my separate blog post.
  • Q: Singleton pattern — what issues do you know?

java generics wild cards – too many warnings/errors

Intro: If your project requires generic wild cards that’s too hard for your team’s knowledge level, then sooner or later you need to make a choice.

The complexity may grow out of hands. The compiler errors are non-trivial. Worse still, some Generics errors are runtime errors.

Sugg: see if you can remove generics completely from some classes. Use cast instead.

I feel in most cases, you only need to use "extends" and not "super". I think it can still be too hard.

Here’s one of my projects — the EventQueue project in the 2017 HSBC coding interview. I had to use generic wildcards like

Subscriber<T extends BaseMessage>

SubsriberFilter<T extends BaseMessage>

CallbackTask<T extends BeaseMessage>

When we pass these objects into methods, we face annoying compiler errors or warnings. Most warnings are unnecessary warnings (I think compiler is not smart enough).

Some methods are designed for BaseMessage like …

Other methods are often designed for “T extends BaseMessage”

Yet other methods are designed for a specific subtype PriceMessage.

I feel it’s often easier to use the BaseMessage as argument type. If un-compilable, I often remove the type parameter.

Small tip: if “instanceof ArrayList” gives generics warning, then use ArrayList.class.isInstance().

small tip: use Subscriber<?> can sometimes suppress a warning

small tip: some casts can suppress a warning

RMI class bytecode sync

Consider an object serialized and sent from hostA to hostB.

If the object is not a standard type like String or a collection, how can the receiving hostB reconstruct it? The class bytecode needs to be sent!

[[java the good part]], written by an RMI authority, gave explicit examples. The serialized stream includes metadata on the data object, which describes where to locate the corresponding class bytecode (of course on a bytecode server). On the receiving end, hostB would attempt to load the class bytecode locally. Failing that, hostB would download the bytecode.

Thanks to dynamic class loading, hostB can reconstruct an exact replica.

java interfaces have only abstract method@@ outdated]java8

Compared to c#, java language design was cleaner and simpler, at the cost of lower power and flexibility. C++ is the most flexible, powerful and complex among the trio.

There was never strong reason to disallow static methods in an interface, but presumably disallowed (till java 7) for the sake of simplicity — “Methods in interface is always abstract.” No ifs and buts about it.

With “default methods”, java 8 finally broke from tradition. Java 8 has to deal with Multiple Inheritance issue. See other blog posts.

In total, there are now 4 “deviations” from that simple rule, though some of them are widely considered irrelevant to the rule.
1) a concrete nested class in an interface can have a concrete method, but it is not really a method _on_ that interface.
2) if an interface MyIFace re-declares toString() method of Object.java, that method isn’t really abstract. There’s very few reasons to do this. 3) static methods
4) default methods — the only real significant deviation from the rule

JVM = a bytecode interpreter + JIT compiler

I used to think the JVM is a layer on top hardware and executes platform-independent bytecode against the hardware. The hardware components include

  • filesystems
  • network ports
  • CPU and memory
  • kernel threads
  • user input devices + screen

Consider assembly code. I guess assembly code deals directly with the same hardware components, with possible exception of threads.

(Not sure where the operating system kernel comes into play. See https://bintanvictor.wordpress.com/2011/09/08/what-is-kernel-space-vs-userland/)

Now I think JVM includes a JIT compiler that converts bytecode into assembly. See https://bintanvictor.wordpress.com/2016/02/09/javac-jit-2-compilers/

 

java local var(!fields)need explicit initialization

http://stackoverflow.com/questions/268814/uninitialized-variables-and-members-in-java briefly mentions the reason.

Instance variables (i.e. fields) of object type default to being initialized to null. Local variables of object type are not initialized by default and it’s a compile time error to access an undefined variable.

For primitives, story is similar