Just by declaring byte as final, why casting is not needed to assign byte to char ??
Explicit casting is needed between primative types when the ranges of the source doesn't fit into the target's range. For byte and char, explicit casting is need for both directions.
Declaring bytes as final is not enough to allow implicit casting. The byte has to be defined as a compile time constant. When it is a compile time constant, the compiler can figure out the value of the byte, to check to see if it can be casted implicitedly.
It so happens that your test generates a compile-time constant when it is declared final. If it was a method call, or uses a variable that isn't a compile time constant, declaring it as final would not work.
One small point is sometimes forgotten. Mainly because it is so obvious that you never think about it: By declaring a variable final (in a compile time constant way) you can only omit the casting when the value of the variable "fits into the container".
So: final byte b = -5; char c = b;
The last line will not compile. b is a compile time constant, but chars can't hold negative numbers. When you casted against char, it worked - with an overflow.