Generics List<String> and List<Integer> not behaving as expected - java

Why is the println printing "tom" and not showing any runtime exception after casting to List<Integer>, while it is not able to print the value 1 after casting to List<String>?
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String args[]) {
List list = Arrays.asList(1, "tom");
System.out.println(((List<Integer>) list).get(1));
// "tom"
System.out.println(((List<String>) list).get(0));
// ClassCastException: Integer cannot be cast to String
}
}

The first call of println is statically dispatched to PrintStream.println(Object) and the second call is dispatched to PrintStream.println(String). So for the second call the compiler puts an implicit cast to String which then fails with ClassCastException at runtime.

The problem here is that the java compiler picks methods at compile time, not runtime.
And at compile time it will pick the method PrintStream.print(String), not PrintStream.print(int) or PrintStream.print(Object), both of which would succeed.

Integer i = new Integer(101);
String s = new String(i); // undefined and Invalid
StringBuffer sb = new StringBuffer(i); // defined and Valid
String s2 = "tom";
Integer i2 = new Integer(s2); //defined and valid
So when you assign a non generic list to a generic one it is assigned but when you are printing it it checks for type safety or define constructors for casting if there are valid and defined constructors then it is printed else shows class cast exception as the class can not be casted due to lack of undefined constructors for casting.
If I am wrong please help me out with the correct logic...

This type of problem can be avoided by using generics and is the primary motivation for using generics.
This is the actual flow of your code, from your second println() point of view:
your code declares an ArrayList of type Object;
It adds an Integer and a String to the ArrayList.
It cast your list to a String list. Your list is marked as being restricted to String.
Java generics are a compile-time feature only so your list can accepts without any problem String and Integer elements. The object itself knows nothing about what types it's supossed to contain unlike to the compiler.
It attemps to retreive the first element of your casted list which is supposed to be a String and cast it to String implicitly.
Calls println(String x) from PrintStream class.
But this first element is actually not a String and is an Integer.
You cannot cast an Integer to a String.
Read Generics in Java motivation section example.

Related

HashSet cannot be converted to String error with instanceof operator

I have these nested hashsets, in which the inner contain String values.
{{a,b},{b,c},{c,e}}
At one point in my code, I do not know whether I am dealing with the inner hashset or the outer one. I am trying to ascertain by using the following line of code:
System.out.println(loopIterator3.next() instanceof String);
//(FYI :Iterator <HashSet> loopIterator3 = hsConc2.iterator();)
This line of code seems to generate an error:
prog.java:61: error: incompatible types: HashSet cannot be converted to String
System.out.println(loopIterator3.next() instanceof String);
When loopIterator3 is indeed traversing an inner hashset, i would expect
it would be taking String values. Why does the compiler think it is a hashset?
Moreover, why does the compiler think I am trying to convert?
Any thoughts/suggestions?
import java.util.Arrays;
import java.util.HashSet;
class Scratch {
public static void main(String[] args) {
HashSet<HashSet<String>> hashSets = new HashSet<>(Arrays.asList(newSet("a", "b"), newSet("b", "c"), newSet("c", "e")));
System.out.println(hashSets.iterator().next() instanceof String); //error
System.out.println(hashSets.iterator().next().iterator().next() instanceof String);
}
private static HashSet<String> newSet(String... str) {
return new HashSet<>(Arrays.asList(str));
}
}
If a cast of the RelationalExpression to the ReferenceType would be
rejected as a compile-time error, then the instanceof relational
expression likewise produces a compile-time error. In such a
situation, the result of the instanceof expression could never be
true.
Source:- https://docs.oracle.com/javase/specs/jls/se7/jls7.pdf (Page 513)
For example a instanceof B (a is object of class A) , if A and B are not in same heirarchy i.e they are not subclass or superclass of either then this is compile time error because if they are not in same heirarchy then it is not possible that a is instanceof B. So compiler shows error at compile time.
In your case HashSet is not a subclass or superclass of String and vice versa, therefore it is showing compile time error
This error is because HashSet and String are not related. I see that you already know the type of object returned by next() method. I did not understand the purpose. Still if you need this check, try something like below-
Object obj = loopIterator3.next();
String.class.isInstance(obj);

When returning an ArrayList do I have to return the <Type> along with it?

Specifically, if I return a filled ArrayList do I have to return the type with it such as ArrayList<modNode>? To add onto this, if i'm using a generic typing for a custom link list that uses the <T> tag would I have to return ArrayList<modNode<T>>? Just a little curious on the properties of ArrayLists containing generic objects... Thanks in advance!
Let's say you have a method that returns an ArrayList of String objects:
public ArrayList<String> foo() {
ArrayList<String> list = new ArrayList<>();
// ... fill list ...
return list;
}
This is how you would normally1 declare the method in Java 5+ (since the addition of generics). But you don't have to do this. You could declare the method as:
public ArrayList foo() {
// ... code ...
return list;
}
This would mean you are using raw types. Unless you are interacting with a legacy (pre Java 5) library/application you never want to use raw types. The reason is because a raw type is (nearly?) equivalent to returning ArrayList<Object>. You've just lost all type safety given by generics. The reason for generics is to provide compile-time2 type checks so you don't accidentally use the wrong types. For instance, you could now add a Dog to the ArrayList returned by foo even though you intended it to only contain String objects. Also, code using the returned ArrayList has no guarantee that there will only be String objects inside the ArrayList which can result in all sorts of headaches.
You could get around raw types by casting:
String element = (String) foo().get(0);
However, that's basically what generic code compiles down to anyway; except you no longer have compile-time safety.
If the element type is also generic then yes you would want to return that information as well. Let's say you return an ArrayList of Supplier objects instead. It will be each Supplier that returns the needed String objects.
public ArrayList<Supplier<String>> foo() {
// ... code ...
}
It's important you give the Supplier's generic signature here so you can do things like:
for (Supplier<String> sup : foo()) {
String str = sup.get();
// .. do something with "str" ...
}
If you returned ArrayList<Supplier> then each Supplier.get() would return an Object. You've lost all type safety again.
1. You would actually, in virtually all cases, want to return List rather than ArrayList. It's best to program to an interface.
2. It only works at compile-time due to type erasure. Also see this.
The type parameter <T> depends on the actual Type Parameter you supply to Generic Type. For example:-
List<String> names = new ArrayList<>();
String is the actual type parameter of parameterized type List<String>. Behind the scene compiler did casting on each elements automatically. So you can safely expect get method will return String type.
The modNode class is generic type. Then caller has to declare what is the actual type parameter. It could be String, type that extends Node or whatever. Example below:-
List<modeNode<String>> modeNodes = new ArrayList<>();
However your ArrayList<modNode<T>> actual type parameter is already modeNode. Hence get method will probably returns some kind parameterized type modeNode<T>. Ex:-
List<modeNode<String>> modeNodes = new ArrayList<>();
....
modeNode<String> mn = modeNodes.get(0);
Notes:-
** Rename modNode type class name to ModNode to follow Java convention. Class name must start with Capital letter.
ModeNode<Node> mn = ModeNode.getInstance();
//More readable due to following naming convention.
List<ModeNode<Node>> mns = new ArrayList<>();
** It is preferable to declare as interface type Listthan concrete type ArrayList. Unless if you want to use specific ArrayList behaviour implementation.

Using an ArrayList<Class?> for casting?

Previous question
I have the following code:
ArrayList<Object> list = new ArrayList<Object>();
list.add("StringType");
list.add(5);
list.add(new RandomClass());
List<Class<?>> classes = new ArrayList<>();
classes.add(String.class);
classes.add(int.class);
classes.add(RandomClass.class);
for (int i = 0; i < list.size(); i++) {
if (classes.get(i).isInstance(list.get(i))) {
...
}
}
if (isvalid)
mymethod(...);
public void mymethod(String string, int num, RandomClass randomClass){ }
Now I'm trying to cast the object into the right type with a method using a string argument.
Instead of:
mymethod( (String) list.get(0), (int) list.get(1), (RandomClass) list.get(2) );
I would like to reuse the definition created above for the cast.
mymethod( ( define.get(0) ) list.get(0), .... );
I've also tried using the Class.cast(obj) but of course it returns a type '?' which again defeats the purpose of casting it again using (String).
What is type safety?
In computer science, type safety is the extent to which a programming
language discourages or prevents type errors.
If code is type safe, then the compiler can verify, at compile time, that all the types are correct:
String getName() {
return "name";
}
The compiler knows that "name" must be a String so it can verify that this code will never throw a type error.
Once you do something like:
int getNumber() {
(int) number;
}
The need to explicitly cast to int tells you that this code has an error condition, namely when number is not of type int or a type that is assignable to int.
How does it affect you?
Your code:
define.get(0).cast(list.get(0))
You want the return type of this statement to be of the type of get(0). But the compiler has no way of knowing, at compile time, what define.get(0) returns. This is inidcated to you by the return type.
You have a List<Class<?>>, i.e. a List of a class of "I don't care what type". You then use a member of this List to cast a member of your other List - the only result can be an "I don't care what type".
You can hack around this with:
<T> T get(final int i) {
return (T) define.get(i).cast(list.get(i));
}
This will happily compile:
final String thing = get(0);
As will:
final int thing = get(0);
i.e. all that you have done is to endogenise the cast. The error condition still exists.
define.get(0).cast(list.get(0)) would attempt to cast list.get(0) to the required type.
In order to be able to select the appropriate method, the compiler needs to know at compile time what the types of the arguments are. Or at least a general category such as List<?> etc.
This is needed to support overloading of methods. There can be many methods with the same name, but with different parameter types.
Since you are asking the VM to call a method when it can't determine which exact method you want to call, because it doesn't know at compile time what the types of your parameters are, what you ask cannot be done in Java.
Here is the relevant section from the Java Language Specification.
What it says is that the system selects at compile time which method signature to use, and then, at run time, the particular implementation of that method signature that's correct for the given instance.
You don't actually need to store object's class separately
list.get(0).getClass()
will get you the class of the stored object and then you can use what #Eran suggested
and
list.get(0).getClass().getName()
will get you the String name of your class

Java generics - mixing types allowed?

I was running some tests earlier and could not find an explanation as to why this code does what it does:
public class Test {
public static void main(String[] args) {
List<Integer> list = new ArrayList(Arrays.asList(Double.valueOf(0.1234)));
System.out.println(list.get(0)); //prints 0.1234
Object d = list.get(0);
System.out.println(d.getClass()); // prints class java.lang.Double
System.out.println(list.get(0).getClass()); // ClassCastException
}
}
That raises a few questions:
why does the List<Integer> accept a Double in the first place (should it compile at all)?
why does the second print work and not the third, although it looks like they are doing the same thing?
EDIT
I understand the following 2 statements:
List aList = new ArrayList(); //I can add any objects in there
List<Integer> aList = new ArrayList<Integer>(); //I can only add something that extends Integer in there
But I don't understand why this one is authorised and why it actually works to some extent at runtime although some operations produce a ClassCastException - I would have expected a ClassCastException at the first line of the code posted above:
List<Integer> aList = new ArrayList(); //I can any objects in there
This:
new ArrayList(Arrays.asList(Double.valueOf(0.1234)))
creates a raw (untyped) ArrayList, in to which you can place anything. This is the correct way to do it:
new ArrayList<Integer>(Arrays.asList(Double.valueOf(0.1234)))
which should now not compile.
If you write
... new ArrayList<Integer>(...
instead it will cause a compiler exception.
On why it works:
System.out.println(list.get(0)); //prints 0.1234
The method Object.toString() is the same in Double and Integer (And because System.out.println() expects an Object this is not cast into an Integer (the compiler optimized the cast away))
Object d = list.get(0);
System.out.println(d.getClass()); // prints class java.lang.Double
Same goes for .getClass(). Here the optimizer again dropped the cast.
System.out.println(list.get(0).getClass()); // ClassCastException
This actually creates an Integer from the list and that fails. It does the cast because the optimizer thought it need to do it, because its not obvious that it doesn't need to.
If you would change that last line to:
System.out.println(((Object)list.get(0)).getClass());
it works :)
The second one does not work because when you use generics the compiler inserts the casts for you (you don't have to). The compiler tries to cast the element to Integer because that is the generic type of the list.
Because you added to the list via an unchecked add, something is now in the list that was not checked when it was going in, thus it fails on coming out.

Why do you name the class twice during instantiation in Java?

When you instantiate an object, why do you specify the class twice?
OddEven number = new OddEven();
Why can't you just say number = new OddEven();? When I declare a string, I only say String once:
String str = "abc";
Actually, my question is not "why do you do it this way" -- obviously, you do it because you have to -- but rather, why did the creators choose to make Java syntax work like this?
My thoughts are:
There is something fundamental to the way Java operates at a low level that necessitates typing the name twice, or
The creators freely choose to do it this way to keep some aspect of the syntax uniform -- declare the type first? Or was it to be more like its predecessors?
Because you can do this:
Superclass x = new Subclass();
The type of the reference can be a superclass of the actual object being declared, so you need to specify both. For example, you can do:
List<String> stringList = new ArrayList<String>();
Your program interacts with objects that implement List, and you don't care about the implementation.,
The reason for the seemingly redundant type name is that you are performing two separate operations, each of which requires you to specify a type.
On the left side, you are declaring a variable (a storage location) with a particular type. On the right side, you are creating a new object with a particular type. The '=' in the middle causes a reference to the new object you created to be placed in the storage location you created.
The types on each side do not have to be the same. This, for example, is legal code:
Object number = new OddEven();
The reason that the keyword String only shows up once in your second example is that the type String is implied on the right hand side since "xxx" is a String constant. It is simply shorthand for:
String string = new String("xxx");
When you write:
OddEven number = new OddEven();
You actually do two things : 1) you declare a variable number of type OddEven and 2) you assign a reference to a new instance of class OddEven. But because a variable can hold any subtype of a type, writing number = new OddEven(); wouldn't be enough for the compiler to know the real type of the number variable. So, you have to declare it too. Java is a strongly typed language, which means that every variable and every expression has a type that is known at compile time. You may want to read the whole Chapter 4. Types, Values, and Variables of the Java Language Specification (JLS) to learn more on this.
Now, when your write:
String str = "abc";
Things are a bit different. Characters enclosed in double quotes, "abc" here, are called a string literal which is already a reference to an instance of String and always refers to the same instance of class String. Quoting the section 3.10.5 String Literals of the JLS:
Each string literal is a reference
(§4.3) to an instance
(§4.3.1, §12.5) of class
String (§4.3.3). String
objects have a constant value. String
literals-or, more generally, strings
that are the values of constant
expressions (§15.28)-are
"interned" so as to share unique
instances, using the method
String.intern.
So, String str = "abc"; is certainly not converted into String str = new String("abc"); which is absolutely not equivalent as I've read in some comments and answers. Running the following class:
public class Test {
public static void main(String[] args) {
String one = "abc";
String two = "abc";
String abc = new String("abc");
System.out.println(one == two);
System.out.println(one == abc);
}
}
Produces the output below:
true
false
And demonstrates that one and two are references to the same instance but that abc is a reference to another instance (i.e. an extra unnecessary object has been created).
Actually, using new String(String) is a inefficient way to construct new strings and should only be used to force a substring to copy to a new underlying character array, as in
String tiny = new String(monster.substring(10,20))
Think of 'OddEven number' as defining the Object and 'new OddEven();' as filling the object.
I'm not going to get into detail about superclasses and subclasses because other people have explained it already.
When you say String name = "foo", internally Java compiler creates a String object with the value "foo" and it assigns its reference to name variable. So, here instead of creating a new String object, we are assigning a reference to the other String object.
Btw, the compiler anyway creates "foo" for us. It first looks in String Pool, if it not exists, only then it creats "foo". Otherwise, Compiler returns a reference from String pool. This is some optimization that Java compiler performs internally.
String name = "foo" is simlar to OddEvenNumber oddEven = anotherOddEvenObject;
Array example:
declaration and initialization - when you know the length of the array:
int[] numberArray = new int[10];
declaration then initialization - when you don’t know the length of the array yet and might get it from a method or user input
int[] numberArray;
int length = 10; // let’s say we got this from the user
numberArray = new int[length];
Initialization only - when you don’t need to reuse:
return new int[10];
First OddEven is the type, second is the instance. It need not be even OddEven, it could be any subclass of OddEven. It does not mean you have type it twice. Any IDE have code templates where you have to type the name only once.
The first declaration is the type of variable you want to use in the scope you have, in this case it is OddEven, the second declaration is the constructor to use for instantiating (and in this case initializing) the reference.
You could have said INumberInstance = new OddEven(), where INumberInstance is some class which OddEven can be cast to (like a super of OddEven for instance).
The way to create a new object in java is:
Class_name reference_variable = new Class_name(param_if_any);
But the string class is an exception.
You can create a new string object as
String s = "abc";
or
String s = new String("abc");
Further to what Jim said, Java is a statically typed language. That means that every varable has a type that is know at compile time.
For instance:
public class A
{
public void foo() { }
}
public class B
{
public void foo() { }
}
public class Main
{
public static void main(final String[] argv)
{
A a = new A();
B b = new B();
a.foo();
b.foo();
}
}
the compiler looks at "a.foo()" and "b.foo()" and checks to see that a is of type A and A has a method called "foo" that takes no arguments. The compiler does the same for "b.foo()".
If you could write main like this:
public class Main
{
public static void main(final String[] argv)
{
a = new A(); // in Java you would really do Object a = new A();
b = new B(); // in Java you would really do Object b = new B();
a.foo();
b.foo();
}
}
then the compiler could not do that verification and it would have to happen at runtime.
The designers of Java did not have to make the syntax redundant. Scala is another language using the JVM, and it's also statically typed. Scala uses type inferencing to cut out verbosity. For instance here's a declaration of a variable of type MyPair called x. MyPair associates two variables with each other. It's a generic class, so you can specify that the first variable have type Int and the second the type String:
var x: MyPair[Int, String] = new MyPair[Int, String](1, "scala")
Scala type inferencing lets you remove the redundant type declaration:
var x = new MyPair[Int, String](1, "scala")
And Scala even infers types based on the constructor arguments, so you can write it this way:
var x = new MyPair(1, "scala")
Consider the following example,
We can specify the object type as follows,
List<String> abc;
In method1(), if you want to use array list which suits the best for it requirements then we can instantiate like as follows,
abc = new ArrayList<String>();
In method2(), if you want to use Linked array list which suits best for it requirement then we can instantiate like as follows,
abc = new LinkedList<String>();
So, the idea is that we can specify the type of "SuperClass" and instantiate with whatever subclass suitable for different requirements like "LinkedList" and "ArrayList" in the appropriate operation dynamically.

Categories