Casting Objects in Java

Different methods for casting an object in Java

The second method that you show is not casting; it's simply calling the toString() method on an object, which is not different than any other method call:

String str2 = o.toString();

The effect of the first and third methods is essentially the same. I would prefer using the first method.

What happened with object on the time of casting internally?

Nothing happens to the object. Casting is not a way to somehow automatically convert objects from one type to another type. The only thing that a cast does, is tell the compiler to accept the assignment statement and not check the types for this statement. You're saying to the compiler "I know better than you what kind of object this is, so let me do this assignment and don't complain about the type".

In your example, the type of the variable o is Object. When you assign o to a variable of type String, the compiler won't allow it because it checks the types, and it can't be sure that o in fact refers to a String object. So you use a cast to tell the compiler "I know this is a String object, so let me do this assignment".

The type will still be checked, but at runtime, not at compile time. If, at runtime, the type of the object is not String, you'll get a ClassCastException.

How to cast one object into another

You cannot cast objects at all in Java. You can cast a reference, to a type implemented by the referenced object.

What you can do is convert from one object to a new object. If you cannot modify the classes, you can write an external converter. For example:

public class EmployeeFactory {
public static Employee toEmployee( User user ) {
Employee emp = new Employee();
emp.setUserId( user.getUserId() );
emp.setUserName( user.getUserName());
return emp;
}
}

Cast an Object into a class

When you assign an object reference to a variable, any further operations you'll do with the reference variable are limited to the ones that are defined for its type.

So you can assign a Fish to an Object variable. But as long as you use the Object variable, you can only use methods that are defined in Object, such as toString(), hashCode() etc.

But you noted in your comments that you want to run a similar method in two classes. I assume that means your Dog and Fish classes have a method with similar names, parameters and return type, for example getLegCount() which would return 4 for Dog and 0 for Fish.

When this situation arises, you either have to declare both types as subclasses of another class, that has the required method:

public abstract class Animal {

public abstract int getLegCount();

}

class Fish extends Animal {

@Override
public int getLegCount() {
return 0;
}
}

And then the return type from your getInstance() should be Animal, not Object. And you put the result in an Animal variable, and then you can use the getLegCount() method.

Another option is to define the required common methods as an interface. It works the same way, and is preferable if there are no implementation details in the expected Animal type, only method definitions:

public interface Animal {

int getLegCount();

}

class Fish implements Animal {
@Override
public int getLegCount() {
return 0;
}
}

Again, you would declare your return type from getInstance as Animal and your variable as Animal rather than Object.

How does the JVM actually cast objects and issue a ClassCastException?

The JVM has a bytecode, checkcast, which is used to check if a cast can be validly performed. The actual cast check semantics are described in the JLS§5.5.3, and the details of the checkcast bytecode are described in the JVM spec§6.5. As an example,

public static void main(String args[]) {
Number n = Integer.valueOf(66); // Autoboxing

incr((Integer) n);

System.out.println(n);
}

produces

 public static void main(java.lang.String[]);
Code:
0: bipush 66
2: invokestatic #3 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: astore_1
6: aload_1
7: checkcast #4 // class java/lang/Integer
10: invokestatic #5 // Method incr:(Ljava/lang/Integer;)V
13: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
16: aload_1
17: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
20: return

Additionally, by delving into Hotspot's source code we can see two implementations of checkcast, one used in production and another used for simple test and early ports.

First shown is the production template-based interpreter (thanks to apangin for making me aware of it) which generates code that corresponds to a null check of the reference to be cast-checked, the loading of the class information, a call to a subtype check, and a possible jump to code that throws a ClassCastException:

void TemplateTable::checkcast() {
transition(atos, atos);
Label done, is_null, ok_is_subtype, quicked, resolved;
__ testptr(rax, rax); // object is in rax
__ jcc(Assembler::zero, is_null);

// Get cpool & tags index
__ get_cpool_and_tags(rcx, rdx); // rcx=cpool, rdx=tags array
__ get_unsigned_2_byte_index_at_bcp(rbx, 1); // rbx=index
// See if bytecode has already been quicked
__ cmpb(Address(rdx, rbx,
Address::times_1,
Array<u1>::base_offset_in_bytes()),
JVM_CONSTANT_Class);
__ jcc(Assembler::equal, quicked);
__ push(atos); // save receiver for result, and for GC
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::quicken_io_cc));
// vm_result_2 has metadata result
__ get_vm_result_2(rax, r15_thread);
__ pop_ptr(rdx); // restore receiver
__ jmpb(resolved);

// Get superklass in rax and subklass in rbx
__ bind(quicked);
__ mov(rdx, rax); // Save object in rdx; rax needed for subtype check
__ movptr(rax, Address(rcx, rbx,
Address::times_8, sizeof(ConstantPool)));

__ bind(resolved);
__ load_klass(rbx, rdx);

// Generate subtype check. Blows rcx, rdi. Object in rdx.
// Superklass in rax. Subklass in rbx.
__ gen_subtype_check(rbx, ok_is_subtype);

// Come here on failure
__ push_ptr(rdx);
// object is at TOS
__ jump(ExternalAddress(Interpreter::_throw_ClassCastException_entry));

// Come here on success
__ bind(ok_is_subtype);
__ mov(rax, rdx); // Restore object in rdx

// Collect counts on whether this check-cast sees NULLs a lot or not.
if (ProfileInterpreter) {
__ jmp(done);
__ bind(is_null);
__ profile_null_seen(rcx);
} else {
__ bind(is_null); // same as 'done'
}
__ bind(done);
}

The simple non-production interpreter can show us another example at bytecodeInterpreter.cpp line 2048. We can actually see what happens in a sample compliant bytecode interpreter when a checkcast is reached:

  CASE(_checkcast):
if (STACK_OBJECT(-1) != NULL) {
VERIFY_OOP(STACK_OBJECT(-1));
u2 index = Bytes::get_Java_u2(pc+1);
if (ProfileInterpreter) {
// needs Profile_checkcast QQQ
ShouldNotReachHere();
}
// Constant pool may have actual klass or unresolved klass. If it is
// unresolved we must resolve it
if (METHOD->constants()->tag_at(index).is_unresolved_klass()) {
CALL_VM(InterpreterRuntime::quicken_io_cc(THREAD), handle_exception);
}
Klass* klassOf = (Klass*) METHOD->constants()->slot_at(index).get_klass();
Klass* objKlassOop = STACK_OBJECT(-1)->klass(); //ebx
//
// Check for compatibilty. This check must not GC!!
// Seems way more expensive now that we must dispatch
//
if (objKlassOop != klassOf &&
!objKlassOop->is_subtype_of(klassOf)) {
ResourceMark rm(THREAD);
const char* objName = objKlassOop->external_name();
const char* klassName = klassOf->external_name();
char* message = SharedRuntime::generate_class_cast_message(
objName, klassName);
VM_JAVA_ERROR(vmSymbols::java_lang_ClassCastException(), message);
}
} else {
if (UncommonNullCast) {
// istate->method()->set_null_cast_seen();
// [RGV] Not sure what to do here!

}
}
UPDATE_PC_AND_CONTINUE(3);

In a nutshell, it grabs the argument off the stack, gets the Class object from the constant pool (resolving if necessary), and checks if the argument is assignable to that class. If not, it gets the names of the object's type and of the class to which the cast was attempted, constructs an exception message, and throws a ClassCastException with that message. Oddly enough, the mechanism for throwing a ClassCastException is not the same as that used for the athrow bytecode (using VM_JAVA_ERROR instead of set_pending_exception).

Response to edit: it would be better to just use the type system and OOP principles instead of odd Java internals. Just have a Pipe class (extending Object) that has a getInputStream and a getOutputStream method, each of which returns an instance of a corresponding inner class (i.e. Pipe$PipeInputStream and Pipe$PipeOutputStream, both of which access the private/protected state of Pipe)

How can I cast objects that don't inherit each other?

Casting take place from child to parent (downcast) or vise versa (upcast):

class A extends B
B b = (B)(new A());

or in case of interfaces:

List<String> myList = new ArrayList<>();
ArrayList<String> myArrayList = (ArrayList)myList;

Be careful when casting - if casting is not possible, you'll receive Exception!

In your case, mapping is what you're looking for. You simply need a mapper.

For example:

public class AToBMapper {
public static A fromB(B b) {
A a = new A();
a.setA(b.getA());
a.setB(b.getB());
return a;
}

public static B fromA(A a) {
//fill in
}
}

Casting Object to a Pair

The major problem is that after you have cast, you are comparing with the original otherObject, which still is an instance of the Object class, rather than the variable on the Left hand side of your assignment aPair which has the type Pair

So, this should get you going:

public boolean equals(Object otherObject)
{
Pair aPair = (Pair)otherOject; //--------> ???

if(aPair.fst.equals(this.fst) && aPair.snd.equals(this.snd))
{
return true;
}
}

Be careful here though. What you are doing is called unchecked casting. Your compiler might warn you about it. You don't know whether the object being passed to your equals method really can be cast to a Pair - it has to be an instance of Pair or a subclass of Pair for that to be a valid action. So, your cast might fail at runtime if, for instance, you pass a String or Integer object into the method.

EIT

@NathanHughes's more complete answer to this question shows you how to check the casting (using instanceof keyword), so I won't repeat it here.

I recommend the Oracle java tutorial docs for this kind of thing. Check this tutorial for classes and subclasses - the very end is about how to cast and check if it is valid to cast.

How to cast a java Object to an arbitrary other type

You can cast a thing that is declared as an Object to any Class or Interface that it actually is, for instance

Object o = java.util.Calendar.getInstance().getTime().toString();
// o is declared as an object but is really a String

// this is fine
String s1 = (String) o;
// this is also fine because String implements CharSequence
CharSequence cs = (CharSequence) o;

Likewise, a method may be declared to return Object while it is returning some specific subclass(es) of Object, then the return can be cast to the specific thing being returned.

However, if a thing is built as an Object it can't be cast to anything; it may be an Object that resembles a String, but it is not an object of class String.

public Object getThing(final Object arg) {
return new Object() {
private String str = arg.toString();
public String getStr() {
return this.str;
}
};
}

The thing being returned here is an Object (is-a) though it's been extended with bits that can't be accessed (except through reflection) because they aren't part of java.lang.Object's contract.

One approach around this kind of thing is Java Generics. If you have public Object decrypt(...) you may be able to create public T decrypt(...) — but then you have to actually build objects of class or interface T, and generics is entirely another subject, too large to go into here.

So, it depends on what is actually being built and returned by your decryptObject(...) method for how you can go about casting the returned object.

To do a static cast as (SomeClass) o you have to already know what the thing is.



Related Topics



Leave a reply



Submit