HashSet cannot be converted to String error with instanceof operator - java

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

Related

Check Vector type

I want to know if Vector is holding <String> or <Integer>.
my function public void printVector(Vector <?> v){
I tried if(v instanceof <String>) but the compiler won't allow it.
whats the issue?
A code example that may be relevant:
import java.util.Iterator;
import java.util.Vector;
public class Cool {
public static void main(String[] args) {
Vector<Integer> v;
v = new Vector<>(5);
v.add(Integer.valueOf(5));
test(v);
}
private static void test(Vector<?> v) {
Iterator<?> iterator = v.iterator();
if (iterator.hasNext()) {
System.out.println(iterator.next().getClass());
}
}
}
This will print out class java.lang.Integer.
Your "test" method will have to check the type against a set of superclasses you're interested in (Employee/Customer/etc).
Also, Using vectors is generally considered bad practice in java.
Theoretically, you cannot know, because of type erasure. Basically, it means that you cannot get the generic type of the vector at runtime.
Now, in a real application, if you know that every objects are of the same type, you can get the first one and check its type with instanceof.
Note : this is not a good practice. Avoid that if you can. And as said by the others, you should consider other collections than Vector.
Note : as a rule of thumb, using instanceof is a clue of design flaw. Also, this is a costly operation.
If there is no first element, then your array is empty so you can just drop it.
As has been pointed out, Java's type erasure will make the compile-time generic type information unavailable run-time.
A common workaround for this is to pass the Class of the generic parameter as a method argument. This way you can then check whether the method was indeed passed a list of Customers or Employees:
public void print(List<?> list, Class<?> clazz) {
if (clazz == Employee.class) {
// ...
} else if (clazz == Customer.class) {
// ...
} else {
// ...
}
}
You could then call the method as follows:
Vector<Employee> legacy = new Vector<>();
// do stuff
print(legacy, Employee.class);
Note that resorting to instanceof or class checks is usually a sign of bad object oriented design and in general, you can achieve a more elegant solution through polymorphism, i.e. overriding the print() method for different object types -- or having a single print() method that utilizes different toString() implementations of the domain objects.

A typical operation on a generic function

I was recently learning generics in Java and a question came in my mind that Python programmers can relate to. All I did was to create a generic Java function which should return a string and take two parameters, "p1 and p2" of different (or similar) generic types. But the tricky thing is I want to return the value of p1 if p1 is of type String, else a general String message... A simple Python implementation of this code will be:
def func1(a, b):
if type(a) == str:
return a
else:
return 'bye'
func1(2, 5) # Output: 'bye'
func1("hi", 5) # Output: 'hi'
I tried the same on a Java function, but it seems to give an error which by my observations can only be rectified if it is forcefully (rather unwantedly, since the obj1 is already checked for being a type of string) typecasted into String:
public static <T, T2> String func3(T obj1, T2 obj2) {
if (obj1.getClass().getSimpleName().equals("String")){
return obj1; // <--error // (String)obj1; <-- is working
}
else {
return "bye";
}
}
So, what is being wrong here? Is it somewhat related to the "ducktyping" feature of Python, and not possible in Java? Or am I using the wrong functions for getting the desired class name?
It is related to the fact that Python is dynamically typed.
Although you checked the type of obj1 here:
if (obj1.getClass().getSimpleName()=="String"){
The compiler forgets about this when it sees the return statement. It will insist that obj1 is of type T but not String, so obj1 can't be returned.
The solution for this is, as you pointed out, cast obj1 to String. Casting is like saying to the compiler, "I am sure this is a string!"
P.S.: You can check if an object is a string by simply doing:
if (obj1 instanceof String)
Usually in Java you can check the type of an object using instanceof:
someObject instanceof Type
is true if the object is a Type or a subclass of Type.
Otherwise, you can do something like this:
someObject.getClass().equals(Type.class)
is true only if the object is a Type
In your case you can use both:
obj1 instanceof String
or
obj1.getClass().equals(String.class)
But I suggest the first solution as in Java is the most used.
In your case I would write a method like this:
public static String func3(T obj1, T2 obj2) {
if(obj1 instanceof String) {
return (String)obj1;
}
else {
return "obj1 not a String";
}
}

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.

Compare object with class stored in field

I am trying to compare part with the c class given from the constructor but for some reason my IDE pops a warning that it will always be false.
Class<? extends Part> c;
public Slot(Class<? extends Part> c){
this.c = c;
}
public boolean addItem(Part part){
return part instanceof c;
}
Why is this instanceof statement always false and what is the correct syntax to compare part with c?
You should call the following Class method:
Class.isInstance(Object obj)
When you call "instanceof c", "c" should be a Class, not a variable. In your example, c is a variable whose type is Class.
Instead of this
if(part instanceof c) return true;
Try this
if(c.isAssignableFrom(part.getClass())) return true;
This is different than Class.isInstance(Object), because isAssignableFrom also returns true when part is the same type as or a sub-class of c.
So if you need to know if this is the exact same type use isInstance, if it could also be a sub-class, then use isAssignableFrom.
It's about understanding the operator instanceof and the Class class.
Type Comparison Operator instanceof:
At run time, the result of the instanceof operator is true if the
value of the RelationalExpression is not null and the reference could
be cast to the ReferenceType without raising a ClassCastException.
Otherwise the result is false.
This means that your code if (part instanceof c) ... will always return false, as part can never be cast to Class.
If you have a look at Class.inInstance(Object obj), you find that
This method is the dynamic equivalent of the Java language instanceof operator.
In other words:
If you know the checked type in compile-time, you use instanceof.
If you do not know the checked type in compile-time, but in run-time (so you have it stored in a variable of the Class type), you use Class.inInstance(Object obj).

Generic allowing String Object as Integer

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...

Categories