What is an Unchecked Call in Java? [duplicate] - java

This question already has answers here:
How to fix unchecked call warning in Java?
(2 answers)
Closed 3 years ago.
public class Main<T> {
T obj;
public Main(T input) {
this.obj = input;
}
void set(T input) {
this.obj = input;
}
void print() {
System.out.println(this.obj);
}
public static void main(String[] args) {
Main<Integer> tester = new Main<>(2);
Main test = tester;
test.print();
test.set(3);
test.print();
}
}
In the code above test.set(3) gives me a warning "Unchecked call to 'set(T)' as a member of raw type 'Main'". What is an unchecked call and why do I get it, even though the set method works and after the print statement is executed, 3 is printed.

You haven't told the compiler what kind of Main the test variable refers to. As far as the compiler is concerned, it could be Main<Integer> or Main<String> or Main<RahatsCrazyClass>. So the compiler can't guarantee that test.set(3) makes sense - you could be trying to set an Integer object inside a Main<String>.
The warning tells you that the compiler has encountered a condition that it can't guarantee the sense of. You should avoid having this kind of thing. Better to declare test as a Main<Integer> than as just a Main.

You are explicitly defeating the purpose of Generics, which implicitly specializes your generic class with Object type argument. In this case, you're defeating type-safety and setter method on the raw type Generic will be unchecked in compile-time, due to late binding. Compiler simply does not know at compile time what type of value is passed into setter method, as there is no type checking.
You should always try not to use raw type specialization in your Generic classes, unless it is really must to do.

Related

What is the purpose of additional diamond parameters in a method signature (that look like an additional return type(s))? [duplicate]

This question already has answers here:
What does the type parameter <T> in the method definition mean? [duplicate]
(1 answer)
What are Generics in Java? [closed]
(3 answers)
Java Generics: Generic type defined as return type only
(6 answers)
Understanding generic parameters with void return types
(5 answers)
Closed 10 months ago.
I have the following class which builds:
public class Test<T> {
public T DoSomething(T value) {
return value;
}
}
I can also define it like this class like this (notice the extra in the DoSomething signature (which also builds):
public class Test<T> {
public <T> T DoSomething(T value) {
return value;
}
}
What is its purpose and when do I need to include it? I am asking about the additional <T> in the return type, not what generics are.
Maybe this will clear it up. The notation <T> declares a type variable.
So we have one variable T at the class level, and a redeclaration of that same symbol for a particular method.
class Test<T> {
<T> T doSomething(T value) {
// <T> declares a new type variable for this one method
System.out.println("Type of value: " + value.getClass().getSimpleName());
return value;
}
T doSomethingElse(T value) {
// T is not redeclared here, thus is the type from the class declaration
System.out.println("Type of value: " + value.getClass().getSimpleName());
return value;
}
public static void main(String... a) {
Test<String> t = new Test<>();
t.doSomething(42);
t.doSomething("foo"); // also works
t.doSomething(t); // contrived, but still works
t.doSomethingElse("hi");
t.doSomethingElse(42); // errors because the type `T` is bound to `String` by the declaration `Test<String> t`
}
}
In main, I create a Test<String> so the class-level T is String. This applies to my method doSomethingElse.
But for doSomething, T is redeclared. If I call the method with an Integer arg, then T for that case is Integer.
Really, it would have been better to call the second type variable anything else at all, on the declaration of doSomething. U, for example.
(In most cases, I actually favour giving useful names to type variables, not just single letters).
The concept is known as a generic method (docs.oracle.com).
In the code presented, we have an especially tricky case of generics since we have two generic parameters with the same name:
the <T> on the class-level: public class Test<T>, and
the <T> on the method-level: public <T> T DoSomething(T value)
The latter hides the former within the scope of the method DoSomething(...), just like a local variable would hide an instance field with the same name. In general, I would advice against this type of "hiding" since it makes the code harder to read and understand. Thus, for the rest of the discussion we will work with this (slightly modified) version of the code:
public class Test<T> {
public T doSomethingWithT(T t) {
return t;
}
public <U> U doSomethingWithU(U u) {
return u;
}
}
The scope of the class-level generic parameter T is for the whole class, while the scope of the method-level generic parameter U is only for the one method it is delared on. This will lead to the following observation:
// T is bound to type String for the instance testString:
final Test<String> testString = new Test<>();
final String tString = testString.doSomethingWithT("Hello");
System.out.println(tString); // prints "Hello"
// will not compile since 1 is not a String:
// int tInt = testString.doSomethingWithT(1);
// For this one invocation of doSomethingWithU(...), U is bound to
// type String:
final String uString = testString.doSomethingWithU("World!");
System.out.println(uString); // prints "World!"
// for this one invocation of doSomethingWithU(...), U is bound to
// type Integer:
final int uInt = testString.doSomethingWithU(1);
System.out.println(uInt); // prints "1"
Ideone demo
Notice that, although doSomethingWithU(...) is a generic method, we did not have to specify the generic parameter, the compiler inferred the type for us. While seldom used, we can also explicitly specify the generic parameter for thie method:
final Test<String> testString = new Test<>();
final Number number = testString.<Number>doSomethingWithU(1);
System.out.println(number);
Ideone demo
(In this example, the explicit generic parameter is not necessary, the code works without it aswell, but there are rare cases where this may be useful or even necessary.)
The following is not strictly necessary to understand generic methods, but more of a curiosity one might find in code and is meant to prime the reader that it is bad practice, should not be used and removed when seen.
It should also be mentioned that the JLS allows us to add generic method parameters on method invocations that do not have any generic parameter. Those parameter do not have any effect:
Object o = new Object();
// Method "hashCode()" on "Object" has not generic parameters, one
// can "add" one to the method invocation, it has no effect on the
// semantics, however
int hash = o.<String>hashCode();
Ideone demo
A remark on the code: In Java, methods should be written in camelCase instead of CamelCase (DoSomething(...) -> doSomething(...))

Generics at Runtime [duplicate]

This question already has answers here:
Java generics type erasure: when and what happens?
(7 answers)
Closed 6 years ago.
There two programs
Why is the first code code working?I expected it to throw a Run time Exception while accessing the elements as String is added instead of Integer
Similarly..
The second code is throwing Run time Exception while accessing the element though it is able to add Integer in the arrayList comfortably despite declaring it to hold String.
In both the codes,We are successful in adding the different Data types,but the problems seems to appear while accessing elements
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<>();
Test.addToList(arrayList);
System.out.println(arrayList.get(0));
}
public static void addToList(ArrayList arrayList) {
arrayList.add("i");
}
}
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList<>();
Test.addToList(arrayList);
System.out.println(arrayList.get(0));
}
public static void addToList(ArrayList arrayList) {
arrayList.add(1);
}
}
You can add elements in both cases due to type erasure. At run time, the class doesn't know it was declared as new ArrayList<String>(), just that it was declared as new ArrayList().
In the println case, the method's compile-time overload resolution comes into play. System.out is a PrintStream, which has several overloads for println. Among these are:
...
void println(Object x);
void println(String x);
The Java compiler will pick whichever of those is most specific. In the ArrayList<Integer> case it's the first one, and in the ArrayList<String> case it's the second. Once it does that, as part of the type erasure handling, it will cast the Object result of the raw ArrayList::get(int) call to the required type, but only if that cast is necessary.
In the case of the ArrayList<Integer> call, the cast is not necessary (ArrayList::get(int) returns an Object, which is exactly what the method expects), so javac omits it. In the case of the ArrayList<String> call, it is necessary, so javac adds it. What you see as:
System.out.println(arrayList.get(0));
is actually compiled to:
System.out.println((String) arrayList.get(0));
But the element isn't a String, and that's what results in the ClassCastException.
Generics only exist during compilation. They are removed from the binarycode after compiling, this is called 'type erasure'. It is done mostly for backwards compatibility reasons.
If you compile without warnings and without manual casting, misuse of the generics is not possible, as it will lead to compiler errors and some warnings.
When you state you expect an ArrayList for your function, without any indication of generics. You disable all the compiletime checks. You should have gotten a warning for this. Any place where a generic parameter is used in that case, will just accept Object.
However when you access the objects using get(), something hiddens happens, a cast.
ArrayList<String> list = new ArrayList<>();
list.add("Hello");
String s = list.get(0);
The last line is rewritten to:
String s = (String) list.get(0);
This fails if the list does not return something of type String.
When you only use generics on the list, it will only have Strings (or your code wouldn't compile). But as you have allowed a non-String object into the list, the type check of a cast will fail.

"incompatible types" compiler error with lambda / method referencing and generics

I stumbled upon an issue when working through some old code, replacing several anonymous classes with either lambda expressions or method references. The problem is a bit hard to explain with words, but I'll do my best, and I've also added a short example illustrating my problem to the best of my abilities below.
My example consists of...
A functional interface, GenericListener, which takes a type parameter V and has a single method "genericCallback(V genericValue)".
A class, CallbackProducer, which takes a type parameter T. This class also has a method to add a GenericListener with type Integer.
A Main class which creates CallbackProducers and adds GenericListeners to them.
When I run CallbackProducer's addIntegerListener method from Main's constructor, I get the compiler error: "incompatible types" whenever i avoid specifying the type of CallbackProducer's T.
The method addIntegerListener only uses GenericListener's V. As far as I know, it doesn't use CallbackProducer's T in any way.
I've put several calls to addIntegerListener + comments in Main's constructor, 3 of which cause compiler errors. But as far as I can see (and according to IntelliJ) all of them should be legal. If you comment out the 3 first calls to addIntegerListener the application will compile and run just fine.
Also, if CallbackProducer didn't use generics, and we removed the type parameter T completely, the 3 first calls to addIntegerListener would compile.
Is there a reason for this behavior? Am I misunderstanding something, or is this a weakness or bug in the java compiler? (I'm currently using java 1.8_51)
Thanks in advance for any clarification!
import javax.swing.*;
public class Main {
public static void main(final String[] args) {
SwingUtilities.invokeLater(Main::new);
}
public Main() {
// Compiler error, type of CallbackProducer's "T" not specified
CallbackProducer producer1 = new CallbackProducer();
producer1.addIntegerListener(this::integerReceived);
// Compiler error, no diamond brackets for CallbackProducer
new CallbackProducer().addIntegerListener(this::integerReceived);
// Also compiler error for lambdas with no diamond brackets on CallbackProducer
new CallbackProducer().addIntegerListener(intValue -> integerReceived(intValue));
// Works because a (any) type for CallbackProducer's "T" is specified
CallbackProducer<Object> producer2 = new CallbackProducer<>();
producer2.addIntegerListener(this::integerReceived);
// Works because of the diamond brackets
new CallbackProducer<>().addIntegerListener(this::integerReceived);
// Lambda also works with diamond brackets
new CallbackProducer<>().addIntegerListener(intValue -> integerReceived(intValue));
// This variant also works without specifying CallbackProducer's "T"
// ... but it is a workaround I'd prefer to avoid if possible :-P
GenericListener<Integer> integerListener = this::integerReceived;
new CallbackProducer().addIntegerListener(integerListener);
}
private void integerReceived(Integer intValue) {
System.out.println("Integer callback received: " + intValue);
}
// A callback producer taking generic listeners
// Has a type parameter "T" which is completely unrelated to
// GenericListener's "V" and not used for anything in this
// example really, except help provoking the compiler error
public class CallbackProducer<T> {
// Adds a listener which specifically takes an Integer type as argument
public void addIntegerListener(GenericListener<Integer> integerListener) {
// Just a dummy callback to receive some output
integerListener.genericCallback(100);
}
}
// A simple, generic listener interface that can take a value of any type
// Has a type parameter "V" which is used to specify the value type of the callback
// "V" is completely unrelated to CallbackProducer's "T"
#FunctionalInterface
public interface GenericListener<V> {
void genericCallback(V genericValue);
}
}
Here's a shortened down version without all the comment clutter and with only two calls to "addIntegerListener", one of which causes compiler error.
import javax.swing.*;
public class Main {
public static void main(final String[] args) {
SwingUtilities.invokeLater(Main::new);
}
public Main() {
CallbackProducer producer1 = new CallbackProducer();
producer1.addIntegerListener(this::integerReceived); // Compiler error
CallbackProducer<Object> producer2 = new CallbackProducer<>();
producer2.addIntegerListener(this::integerReceived); // Compiles OK
}
private void integerReceived(Integer intValue) {
System.out.println("Integer callback received: " + intValue);
}
public class CallbackProducer<T> {
public void addIntegerListener(GenericListener<Integer> integerListener) {
integerListener.genericCallback(100);
}
}
#FunctionalInterface
public interface GenericListener<V> {
void genericCallback(V genericValue);
}
}
All 3 compiler errors are due to the fact that you are using a raw CallbackProducer. When you use a raw CallbackProducer, all type arguments undergo type erasure, such that any T, such as yours, without any upper bound, becomes Object.
Because of this, the addIntegerListener method expects a raw GenericListener as a parameter, something that integerReceived no longer fits. The integerReceived method takes an Integer, not an Object, as a raw GenericListener would supply.
You must supply the angle brackets <> on CallbackProducer to avoid using raw types, as you've done on your subsequent examples.

Why constructor call is not ambiguous in the following example? [duplicate]

This question already has answers here:
Method overloading and choosing the most specific type
(9 answers)
Closed 8 years ago.
class Test {
public Test(Object obj) {
System.out.println("Object");
}
public Test(String s) {
System.out.println("String");
}
public static void main(String[] args) {
new Test(null); //prints String. Why not Object?
}
}
If I add another constructor with argument of type Integer ,or, for that matter any other type, calling new Test(null); results in compilation error - The constructor Test(Object) is ambiguous. Why no error is generated for the above example? On executing it, constructor with argument String is called. Why constructor with argument type Object is not called? How this ambiguity is resolved?
//prints String. Why not Object?
Because compiler choose most specific type.
If I add another constructor with argument of type Integer ,or, for
that matter any other type, calling new Test(null); results in
compilation error - The constructor Test(Object) is ambiguous.
Now String and Integer are in the same level in the object hierarchy, So, compiler can't choose one out of those two
Because it is determined by the most specific type of the parameter.
Since String is subclass of Object, and null is subtype of anything, then the second constructor is called, because String is more specific than Object.
Compiler is designed to pick up the overloaded method that very closely matches the Value sent in parameter.

Java Generics :: failure to provide type safety?

This is a narrow-down example of a case I came across.
Take a look at the code below:
class Holder<T> {
private T t;
public Holder(T t) {
this.t = t;
}
public T getValue() {
return t;
}
}
public class FooMain {
private static Object newHolder() {
return new Holder<Integer>(3);
}
public static void main(String args[]) {
Holder<String> hs = (Holder<String>) newHolder(); // line-18
String s = hs.getValue(); // line-19
}
}
What spooked me is that the inevitable ClassCastException gets thrown on line-19 and not on line-18 !
Therefore, having an object in your code that's of type Holder<String> is not enough to guarantee that getValue will return a String. You also have to examine how this object was constructed!
I understand that Type Erasure plays a role here but I am not sure how wide the implications of the above are. In my particular case the newHolder-corresponding method is defined in an external library and returns java.lang.Object so I have to do these casts.
You will get a warning when this is compiled, saying that you're performing a cast that means the compiler can't guarantee type safety. When you make these casts and get these warnings, you're essentially on your own. Since generics are implemented using erasure in Java the generic type information disappears at runtime - it's purely a compile time construct, and thus if you circumvent this the runtime may not know until later that you've performed an invalid cast.
Generics are compile time tool to check for type safety. In runtime there's no validation, as of type erasure. That's why you get an error getting a String from an Integer.
BTW, you should get a warning when casting. Ignoring the warning may have consequences...

Categories