Generic allowing String Object as Integer - java

The List listofinteger is of type Integer , but accepting String Object and when i do check for instance of Integer , it is giving true , its too strange , can anybody explain what is happening.
All i know is when i send the List listofinteger to method ,it is given reference to List reference variable of no type , when i add it takes the input as String , now when i return the List listofanything to List of Integer type , it should take because its a reference .
Now but i check for instanceof , it is printing true for Integer , but its String.
import java.util.ArrayList;
import java.util.List;
public class TestEx {
List<Integer> listofinteger=new ArrayList<Integer>(); //list of integer type
public static void main(String... args)
{
TestEx f=new TestEx();
f.listofinteger.add(123); //adds integer by wrapping it(auto)
f.listofinteger=f.addelement(f.listofinteger);
if(f.listofinteger.get(1) instanceof Integer);
{
System.out.println("true"); //prints true here
System.out.println(f.listofinteger.get(1));
}
}
List<Integer> addelement(List listofanything)
{
listofanything.add("asdasdasd"); //adding String object
return listofanything;
}
}
I know that the method addelement(List listofanything ) here should be given a type Integer but i am here testing it , to understand the concept of Generics

First of all, as #VJD commented, you have a syntax error - unneeded ; at:
if(f.listofinteger.get(1) instanceof Integer);
About your question, 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 no error adding String to list of Integers..

Your program prints true because of a syntax error in your code, which happens to be legal with a different meaning to what you intended.
Your if statement is
if(f.listofinteger.get(1) instanceof Integer);
The semicolon at the end ends the whole statement, and makes it equivalent to passing an empty block:
if(f.listofinteger.get(1) instanceof Integer) {
// Do nothing
}
After that you have the block that was supposed to be part of the if-condition, but is now just an anonymous block. (You can add braces in your code more or less wherever you want, to separate statements for scoping purposes. They still execute in the standard order).
Putting both of them together, your code is equivalent to:
if(f.listofinteger.get(1) instanceof Integer) {
// Do nothing
}
System.out.println("true"); //prints true here
System.out.println(f.listofinteger.get(1));
and so it should be clear that the last two lines will always be executed, regardless of what the if condition was.
As others have noted, you can fix this by removing the semicolon after the if statement. (And since this is a confusing and hard-to-spot problem, many static analysis tool such as FindBugs will highlight these semicolons as a likely problem.)

You are passing your List as an Generic List...
You need to identify your List as List <Integer> listofanything for your code give an error if you add a string... I changed your addElement code to:
List<Integer> addelement(List<Integer> listofanything)
{
listofanything.add("asdasdasd"); //Error when adding
return listofanything;
}
and an error appeared compiling...

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);

Void methods cannot return a value error while trying to return something in a forEach method

I have a snippet as below
private String getString() {
List<String> stringList = Arrays.asList("s1", "s2");
stringList.forEach(item ->
{
if (item.equals("s1"))
return item;
});
return null;
}
Now I get a compilation error Void methods cannot return a value while I try to return item. I googled and couldn't understand why this is happening and the solution to this problem. How do I return item in the above forEach loop?
forEach is a consumption process. The functions passed to a forEach method are expected to have side effects which do not directly result in a value being returned, like printing the contents of the list, or doing some side operation.
Basically, any function that is a Consumer or method that returns void is fair game here.
Since you're explicitly looking for a value, you can use Stream#findAny() instead.
// You don't do anything with the result you want anyway,
// so I'm simply printing it out. You should determine what you need to do
// with the value you get back.
Optional<String> findResult = stringList().stream().findAny(v -> v.equals("s1"));
findResult.ifPresent(System.out::println);
I think you've misunderstood lambdas. A lambda is an executable statement that may be executed immediately, or deferred; more importantly, it has its own method signature.
In the following code, you've defined an anonymous method. What is its signature?
stringList.forEach(item ->
{
if (item.equals("s1"))
return item;
});
The answer is that your anonymous method has the following equivalent representation:
private void myAnonymousMethod( String item ) {
if ( item.equals("s1"))
return item;
}
Do you now see your mistake? Your anonymous method has a void return type, but you're trying to return a value.
Instead, you could fix this in a variety of ways:
When you find the value you're looking for, save it in a local variable outside the scope of the lambda. Pro: introduces the smallest amount of change to your code. Con: the foreach loop will continue to execute after it finds its first match.
Use a different iterator method, such as findAny as indicated in Makoto's post.
Replace the anonymous method entirely with a normal loop.

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

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

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.

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.

Categories