Java - splitting String problem - java

I've had a small problem with splitting a String in java as follows:
System.out.println(dexListMod.get(selNum-1).getClass().getName());
String dexListTempString = dexListMod.get(selNum-1);
The first line prints the class name of the Object returned from the index of selNum -1 in the Vector dexListMod. This outputs the following in the console:
java.lang.String
The second line defines a String of this same Object to split() later but, and this is the problem, the compiler says that they are incompatible types! Am I seeing this wrong, or is this a contradiction?
Thanks.

I assume that dexListMod is an untyped List i.e. a List of Object in which case the compiler does not know at compile time that dexListMod.get(selNum-1) is a String. At runtime the Object can report that it is actually a String, this is polymorphism in action.
What you need to do is cast it to a type or use a typed List. e.g.
if (dexListMod.get(selNum-1) instanceof String) {
String s = (String) dexListMod.get(selNum-1);
System.out.println(s);
}

The problem is that what matters for Java compiler is the static type of the object, not what it actually is at runtime.
Similarly, the following example is rejected by the Java compiler:
SuperClass a = new SubClass1();
a.SomeMethodInSubClass1ButNotInBaseClass(); // fails
If the compiler allowed this, you could have assigned something else to a like:
SuperClass a = new SubClass1();
a = new SubClass2(); // it doesn't have the method!
a.SomeMethodInSubClass1ButNotInBaseClass(); // would fail at runtime if allowed
In the general case, it's theoretically impossible to find the exact runtime type of a variable at compile time. The compiler remains conservative and simply fails to compile instead of assuming correctness and possibly failing at runtime (dynamic languages like Python usually choose the opposite design decision: assume correctness and potentially failing at runtime).
Your code snippet essentially demonstrates the same thing, where the return type of dexListMod.get method is probably Object, and it returns a String instance (which is derived from Object).
If you are sure about the runtime type of an object, Java requires you to be explicit about it and take the responsibility to manually cast it to the type you expect. Of course, the cast can potentially fail at runtime and throw an exception.

If you have not used Generics, the list will return Object, you will need an explicit cast.
if(dexListMod.get(selNum-1) instanceof java.lang.String){
String dexListTempString = (String)dexListMod.get(selNum-1);
}

The problem is that in the second line it's expected to have a String type. Your vector hasn't been parametrized. So the compiler doesn't know which types of objects you store in it
If you store strings in the vector than you need to cast the value to String type
String dexListTempString = (String) dexListMod.get(selNum-1);

Try this and see what it outputs:
String dexListTempString;
System.out.println(dexListTempString.getClass().getName());

The type of the object returned from that call doesn't have to necessarily be a String. It could return an Object. Even though that Object is actually a String (as shown by your call to dexListMod.get(selNum-1).getClass().getName()), it must first be cast as a String before you can use it as such.

You haven't stated the declaration of dexListMod, but I assume that it its without any generic type parameter (Vector dexListMod) or with a type parameter that is a supertype of String, e.g. Vector<Object> dexListMod. The get method of such declaration does not return java.lang.String, but a supertype which may or may not be assignment compatible with String. The compiler enforces static assignment compatibility and therefore gives you this error message.
Use a cast and eventually a type check to assign the result:
Object val = dexListMod.get(selNum - 1)
if (val == null || val instanceof String) {
// this if contains a check for null because null instanceof Type is always false.
// If you want only non-null Strings, then just use "if (val instanceof String)"
String dexListTempString = (String)val;
// ...
}

Related

Are reference objects with different types any different in Java?

When saying:
String str = "hello";
Object obj = str;
System.out.println(str==obj);
The result is true, because it points to the same objects in memory, which makes sense. But if I say:
obj.indexOf("h");
Or any subclass method, I get "cannot find symbol". They're still pointing to the same object, so what's going on during compile-time that makes reference objects of different types different from each other?
The Object type reference only knows about methods that are part of its public interface.
You have to cast if you know that Object reference is a String type:
int index = ((String) obj).indexOf("h");
Apples and Pears.
The identity check == is performed at runtime. And valid and fine.
The construct obj.indexOf... is a compile time error as class Object just does not have a method indexOf
If you tell the compiler (by means of casting) that obj contains a String, you can get valid code
((String)obj).indexOf("h");
Will compile again

Is it possible to have Runtime type mismatch in Java?

I am reading up OCaml and from wiki, it says:
*its static type system renders runtime type mismatches impossible*
I understand why, but then I think, why is this so special in OCaml (and FP)? How do you cause a runtime type mismatch in, say, Java? e.g.
boolean a = true;
int b = a + 1;
will return an error in compile time.
EDIT 1:
Haskell
func :: Int -> Bool
func i = if i > 0 then True else False
Java
boolean func (int i) {
if (i > 0) return true; else return false;
}
Isn;t it the case that both will guarantee the argument type when the func is called?
In Java, you can cause a Runtime type mismatch like this:
Object i = Integer.valueOf(6);
String s = (String) i;
System.out.println(s);
This will compile, because the compile-time type of i (Object) is allowed to be cast to String, however at Runtime, the actual value of i (6, as Integer) will be incompatible to String.
Given this, a ClassCastException is thrown.
Consider the following code using arrays:
// create an array of strings
String[] strings = new String[10];
// cast it to an array of objects
Object[] objects = strings;
// insert an object into the array
objects[0] = new Object(); // Run-time error occurs here
Java allows this to compile, despite the fact that casting a array of strings to an array of objects array introduces the possibility of run-time errors. Line 8 demonstrates this, causing a run-time exception of a type created specifically for this situation: java.lang.ArrayStoreException: java.lang.Object.
See Java generics and type erasure
That wiki is discussing static type systems in general, and contrasting them with dynamically-typed languages rather than other statically-typed languages. There's nothing specific to OCaml or Haskell about runtime type mismatches that doesn't apply to all statically-typed languages.
Note that impossible is a little disingenuous. Pretty much all statically-typed languages give you the ability to also do runtime typing in a limited way, because certain tasks are extremely difficult without it. In fact, the very paragraph you're quoting lists a couple of those cases, like serialization. Other answers here have provided some good examples in Java. However, the vast majority of your code should be able to easily avoid runtime type mismatches.
In Java, type mismatches are possible. For example, the following throws a ClassCastException.
Object o = 1;
String s = (String) o;
However, the arguments passed to a method are checked by the compiler.
It is impossible to invoke a method with signature
boolean func (int i)
unless i is an int, and it is impossible to invoke a method with signature
boolean func2 (String s)
unless s is a String or null.
Therefore you will never get a ClassCastException at runtime within the body of func2 because s is not a String.
In Java, it is impossible to have a type mismatch between reifiable types (primitive types, non-generic reference types, raw types, types parameterized by all wildcards, or array types whose element type is reifiable).
If you have a variable of a reifiable type, then the value it holds at any point in time is guaranteed to be a value of that type. For reference types, this means that the reference is either null or points to an object whose runtime class is a subtype of the variable's type. This is guaranteed because Java requires a cast when storing a value whose type is not a subtype of the variable's type, and casts to reifiable types are checked casts, which means they are checked at runtime, and if the type is not compatible it will throw an exception rather than let there be a type mismatch.
On the other hand, for non-reifiable types (e.g. parameterized types), it is possible to have a type mismatch (which is called "heap pollution" in Java terminology). This is because casts to non-reifiable types are unchecked casts.
List<String> foo = new ArrayList<String>();
foo.add("hi");
List<?> bar = foo;
List<Integer> baz = (List<Integer>)bar; // unchecked cast
// now there is a type mismatch

Confusion with type erasure rule in Java Generics

I have been into Generics for an hour, I have certain doubts. Lets say I have a class like this :
class Pair<T>
{
public T getFirst() { return first; }
}
Basically my book say this :
Type erasure: The type variables are erased and replaced by their bounding types (or
Object for variables without bounds.)
So according to my book statement the code in JVM should look like :
class Pair
{
public Object getFirst() { return first; }
}
Now if I do :
Pair<String> pair = new Pair<>(); //I use Java 7 diamond syntax here.
pair.getFirst()
Is that again my code need to convert from Object to String in return type of getFirst?
Now conisder :
ArrayList<String> files = new ArrayList<>();
The same book say's (with respect to the above code is) :
....the files contain array of Strings.
I'm very much confused about the type erasure rule with the above example of ArrayList. In this case how come files array know that it has String array? (which is contradicting to type erasure rule)
Edit:
In order to see the resulting code of Pair(that is how type erasure have taken place), how can I use javap tool here?
Am I missing something here?
Your confusion comes from what the compiler does and what the JVM does.
Pair<String> pair = new Pair<>(); //I use Java 7 diamond syntax here.
String first = pair.getFirst();
is equivalent in the JVM to
Pair pair = new Pair();
String first = (String) pair.getFirst();
In the case of Map.get()
Map<Key, Value> map = ...
Value value = map.get(key);
is in the JVM
Map map = ...
Value value = (Value) map.get(key);
Is that again my code need to convert from Object to String in return type of getFirst?
No conversion occurs. The object is not altered. All that happens is there might be a cast check of the reference.
In order to see the resulting code of Pair(that is how type erasure have taken place), how can I use javap tool here?
Use javap -c -classpath . ClassUsingPair
1. Erasure is a process in which Compiler removes the Type parameter and Type arguments from during the compilation from Class and Method, Variables etc...
2. Box becomes Box, this is called raw-type, Raw-type is one where the generic class or interface name will be without type argument.
3. So during run-time one won't find the Generics...its mainly done to make the code that didn't used generics to run without any problem, mainly those that were written before Generics came into existence.
4. Generic Class won't be instantiated, cause it needs its constructor to be initiated, and that wont happen as the original Type parameter wont be there during the Runtime.

Casting variables in Java

I wonder if anyone could tell me how casting works? I understand when I should do it, but not really how it works. On primitive data types I understand partially but when it comes to casting objects I don't understand how it works.
How can a object with the type Object just suddenly be cast to, let's say, MyType (just an example) and then get all the methods?
Casting in Java isn't magic, it's you telling the compiler that an Object of type A is actually of more specific type B, and thus gaining access to all the methods on B that you wouldn't have had otherwise. You're not performing any kind of magic or conversion when performing casting, you're essentially telling the compiler "trust me, I know what I'm doing and I can guarantee you that this Object at this line is actually an <Insert cast type here>." For example:
Object o = "str";
String str = (String)o;
The above is fine, not magic and all well. The object being stored in o is actually a string, and therefore we can cast to a string without any problems.
There's two ways this could go wrong. Firstly, if you're casting between two types in completely different inheritance hierarchies then the compiler will know you're being silly and stop you:
String o = "str";
Integer str = (Integer)o; //Compilation fails here
Secondly, if they're in the same hierarchy but still an invalid cast then a ClassCastException will be thrown at runtime:
Number o = new Integer(5);
Double n = (Double)o; //ClassCastException thrown here
This essentially means that you've violated the compiler's trust. You've told it you can guarantee the object is of a particular type, and it's not.
Why do you need casting? Well, to start with you only need it when going from a more general type to a more specific type. For instance, Integer inherits from Number, so if you want to store an Integer as a Number then that's ok (since all Integers are Numbers.) However, if you want to go the other way round you need a cast - not all Numbers are Integers (as well as Integer we have Double, Float, Byte, Long, etc.) And even if there's just one subclass in your project or the JDK, someone could easily create another and distribute that, so you've no guarantee even if you think it's a single, obvious choice!
Regarding use for casting, you still see the need for it in some libraries. Pre Java-5 it was used heavily in collections and various other classes, since all collections worked on adding objects and then casting the result that you got back out the collection. However, with the advent of generics much of the use for casting has gone away - it has been replaced by generics which provide a much safer alternative, without the potential for ClassCastExceptions (in fact if you use generics cleanly and it compiles with no warnings, you have a guarantee that you'll never get a ClassCastException.)
Actually, casting doesn't always work. If the object is not an instanceof the class you're casting it to you will get a ClassCastException at runtime.
Suppose you wanted to cast a String to a File (yes it does not make any sense), you cannot cast it directly because the File class is not a child and not a parent of the String class (and the compiler complains).
But you could cast your String to Object, because a String is an Object (Object is parent). Then you could cast this object to a File, because a File is an Object.
So all you operations are 'legal' from a typing point of view at compile time, but it does not mean that it will work at runtime !
File f = (File)(Object) "Stupid cast";
The compiler will allow this even if it does not make sense, but it will crash at runtime with this exception:
Exception in thread "main" java.lang.ClassCastException:
java.lang.String cannot be cast to java.io.File
Casting a reference will only work if it's an instanceof that type. You can't cast random references. Also, you need to read more on Casting Objects.
e.g.
String string = "String";
Object object = string; // Perfectly fine since String is an Object
String newString = (String)object; // This only works because the `reference` object is pointing to a valid String object.
The right way is this:
Integer i = Integer.class.cast(obj);
The method cast() is a much safer alternative to compile-time casting.

Why is this generic method unsafe?

The following method generates a warning, but it looks safe to me. I'm sure the problem is with me:
public <S extends CharSequence> S foo(S s) {
return (S) new StringBuilder(s);
}
It looks like this will always return the argument s. Can anyone show an example that would cause this method to throw an exception?
Edit: I'm not particularly interested in the question of whether generics are necessary here. Rather, I'm looking for a demonstration of how this method is unsafe.
It's unsafe because while StringBuilder is a CharSequence, it isn't necessarily of type S (in fact, it almost certainly won't be). You can see this failing just by passing a String into your method. This will construct a StringBuilder using your String as an argument, and then try to cast your StringBuilder to String (which will fail).
There's probably no need to use generics here at all, this should work fine:
public CharSequence foo(CharSequence s) {
return new StringBuilder(s);
}
For example this will compile:
String a = foo("ss");
but it will fail at runtime:
ClassCastException: java.lang.StringBuilder cannot be cast to java.lang.String
since foo returns S, the type of your input parameter (String in this case).
I think that you don't need to use generics here (as skaffman said in his answer):
public StringBuilder foo(CharSequence s) {
return new StringBuilder(s);
}
foo("test");
is enough to make java try to cast a StringBuilder in a String.
Your code is guaranteed o be wrong, can you explain what you're trying to achieve plase ?
The unsafety here lies not within the method itself (though it has its problems, too) but at the call site. The use of S for the input argument's type as well as for the return value tells the compiler, that whatever the type of object may be that is passed to the function, the result has the same type (or a derived type, actually).
Thus, the compiler is allowed to assume, that in the call
foo("hello, world")
the result will be a java.lang.String, while in the call
foo(new StringBuffer("hello, world"))
the result will be a StringBuffer, and so on. In both cases, however, your method does not return what it was supposed to return, namely, an object of the same type as the input argument. Instead, a StringBuilder is returned.
Actually, the only kind of input argument your method will work with is a StringBuilder, anything else will be doomed to crash with a ClassCastException sooner or later, as the compiler might (and often does) insert (hidden) casts at the call sites.
And of course, as others have already pointed out, the use of generics is not really necessary here, anyway.
The return type of your method code is ALWAYS a StringBuilder.
That is because the declared type of the expression 'new StringBuilder(x)' is ALWAYS a StringBuilder, whatever the X.
It is TOTALLY pointless to try and cast this to anything. The "S" information is part of the erasure, which exists only at compile-time and is erased by the time the program runs, that is, run-time. (Casting is a run-time thing exclusively, and casting something to some type/class whose identity has been erased a run-time, is indeed totally pointless.)
my Java is rusty, but would this method not throw whatever exceptions
new StringBuilder(s)
can throw?

Categories

Resources