Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: How to copy an object so it will be from the same subclass?

I try to use a simple example for better undersanding: I've got a class Tool and child classes which are extending class Tool: Hammer, Saw. Both have defined some fields like weight and both are overriding method getCost with own implementation.

    Tool first_tool = new Hammer();
    Tool second_tool = new Saw();

I need a method in Tool class, that will to do a copy of any tool, such way, that first_tool_copy is from the same subclass as first_tool. How can I make this possible? I need something like:

    /* Copy tool, change parameters of copy, the original won't change */
    /* first_tool_copy will be instance of Hammer class */
    first_tool_copy = first_tool.copy
    first_tool_copy.weight = 100

Conclusions: I would like to have some simple copy constructor common for all subclasses.

like image 582
peter.bartos Avatar asked Aug 14 '11 16:08

peter.bartos


People also ask

Can objects from different subclasses be stored in the same array?

Objects from subclasses can be stored in the same array of the parent type.

Does a subclass inherit methods?

A subclass inherits all the members (fields, methods, and nested classes) from its superclass. Constructors are not members, so they are not inherited by subclasses, but the constructor of the superclass can be invoked from the subclass.


1 Answers

There are possibly many solutions for this case, but I believe the simplest one would be using reflection to create the cloned object and copy the fields from the original to the copy. The only requirement this code has is that your subclasses must have a default constructor, but this doesn't look like a real issue anyway.

Here's how it would look like:

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Tool implements Cloneable {

private String name;

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

@Override
public Object clone() {

    try {

        Tool instance = this.getClass().newInstance();

        List<Field> fields = new ArrayList<Field>();

        Class<?> kind = this.getClass();

        while ( kind != null ) {
            fields.addAll( Arrays.asList( kind.getDeclaredFields() ) );
            kind = kind.getSuperclass();
        }

        for ( Field field : fields ) {
            field.setAccessible(true);

            int mod = field.getModifiers();

            if ( !Modifier.isStatic( mod ) && !Modifier.isFinal( mod ) && !Modifier.isNative(mod) ) {
                Object value = field.get( this );
                field.set(instance, value);
            }

        }

        return instance;

    } catch (Exception e) {
        throw new UnsupportedOperationException(e);
    }

}

}

And here's your subclass, that would not have anything special:

public class Saw extends Tool {

private int weight;

public int getWeight() {
    return weight;
}

public void setWeight(int weight) {
    this.weight = weight;
}

}

And a JUnit test case showing how it would work:

public class SawTest {

@Test
public void testClone() {

    Saw original = new Saw();
    original.setName("Some saw");
    original.setWeight( 10 );

    Saw clone = (Saw) original.clone();

    Assert.assertTrue( original != clone );
    Assert.assertTrue( original.getClass().equals( clone.getClass() ) );
    Assert.assertEquals( original.getName(), clone.getName() );
    Assert.assertEquals( original.getWeight(), clone.getWeight() );

}

}
like image 151
Maurício Linhares Avatar answered Sep 21 '22 01:09

Maurício Linhares