
Greetings, dear reader.
Java is an interesting and beautiful language. But sometimes you can write on it that it is better not to see it. But it is still useful to know what happens in such curves as well.
Clean code lovers, please excuse me.
Once upon a time there were a couple of articles on Habré
how not to write on java with interesting java puzzles.
Part 1 ,
Part 2They are very interesting, but, unfortunately, the author did not continue.
I present to your attention another 2 puzzles (there was not enough strength for more. It turns out that writing articles is not so easy.)
')
At the end of the article, of course, there will be answers with explanations, as well as
additional tasks for the strongest.
The first task is cleaned from the wonderful book by Joshua Bloch
Java Puzzlers The book does not teach you how to write code (rather the opposite, how not to, and why), but I personally was extremely interested in reading this. Just for fun. Recommend.
So let's get started.
Conditions
1. Execute cannot be pardoned
public class A { public static class X { public static class Y { public static String Z = "life is good"; } public static CY; } public static class C { public static String Z = "life is pain"; } public static void main(String[] args) { System.out.println(XYZ); } }
What will happen?
- Compile error
- Runtime error
- Will withdraw life is good
- Displays life is pain
In addition, how, without changing the names, [correct the error, if the answer is 1/2 and] output
both lines?
2. Generics such generics
public class B { public static <T> T foo() { try { return (T) new Integer(42); } catch (ClassCastException e) { return (T) "habr"; } } public static void main(String[] args) { System.out.println(B.<String>foo()); } }
What will happen?
- Compile error
- Runtime error
- Will print 42
- Will output habr
And now the correct answers:
1. Execute cannot be pardoned
public class A { public static class X { public static class Y { public static String Z = "life is good"; } public static CY; } public static class C { public static String Z = "life is pain"; } public static void main(String[] args) { System.out.println(XYZ); } }
Displays life is painEverything is wild, but simple. Yes, jls allows it. Priority is always on the field.
Much more interesting - how to get around this and withdraw life is good? I know of three solutions:
- reflection api
- import xy
- Statics can be accessed through an instance: (new XY ()). Z
Task for the top five
The latter method is good, but requires the creation of an object, which is bad. Add a private constructor. And now weak? (without reflection and static import)
public static class X { public static class Y { private Y() {} public static String Z = "life is good"; } public static CY; }
Answer:
2. Generics such generics
public class B { public static <T> T foo() { try { return (T) new Integer(42); } catch (ClassCastException e) { return (T) "habr"; } } public static void main(String[] args) { System.out.println(B.<String>foo()); } }
Runtime error , or more precisely, ClassCastException
So, we all know that in java, generics are nothing more than syntactic sugar, which
limits our capabilities, allowing the
compiler to perform additional type checks. But as soon as the program is launched, all information about the class parameters is lost. Alas, this is not c ++ and not even c #.
Let's look carefully at the code:
System.out.println(B.<String>foo());
The compiler understands that the type of the argument is String, and therefore substitutes the most appropriate println:
public void println(String x)
It is important that determining which version of the overloaded method to use is a task of compilation level, not runtime.
Go ahead.
return (T) new Integer(42);
Cast occurs in runtime, at the moment when generics are no.
What will happen?
Nothing. Just come back Integer.
Well, they arrived - they called printf, which accepts String, and passed Integer to it.
Lessons for the future
- Don't do that
- There is not only println in the world that accepts Object
- Caste to a generic type is a crutch
Task for the top five
Implement a method that throws an arbitrary Throwable (including the checked exception), which does not require either thows or try-catch:
public static void throwWithoutCheck(Throwable t) {
Answer:
Hidden text private static <T extends Throwable> void castAndThrow(Throwable t) throws T { throw (T) t; } public static void throwWithoutCheck(Throwable t) { B.<RuntimeException>castAndThrow(t); }
UPD:
There are still ways to throw without checks. They were not meant as an answer, but, let's say, more “honest”:
Thread.currentThread().stop(new IOException());
Note that stop is @deprecated. The concept of working with streams has undergone significant changes, the
grass has become greener, and the air is cleaner .
As a Stop argument, you can pass the "cause" of the stop. It is clear that in principle it is impossible to conclude this in the try catch block, I hope everything is clear here.
Thanks
orionll for the
example .
Or:
Unsafe.getUnsafe().throwException(new IOException());
Everything is also clear here, just not working with Unsafe and, in this case, everything is absolutely justified.
Thanks
mishadoff for
an example .