class Person implements Cloneable
{
    public Person( String nm )
    {
        name = nm;
    }
    
    public String getName( )
    {
        return name;
    }
    
    public String toString( )
    {
        return getClass( ).toString( ) + " " + getName( );
    }
    
    public boolean equals( Object obj )
    {
        if( obj == null || getClass( ) != obj.getClass( ) )
            return false;
            
        Person other = (Person) obj;
        
        return getName( ).equals( other.getName( ) );
    }
    
    public int hashCode( )
    {
        return getName( ).hashCode( );
    }
    
    public Object clone( ) throws CloneNotSupportedException
    {
        Object copy = super.clone( );
        ((Person)copy).name = name;  // normally call clone
        return copy;
    }
    
    private String name;
}

class Student extends Person 
{
    public Student( String nm, int studentId )
    {
        super( nm );
        id = studentId;
    }
    
    public int getID( )
    {
        return id;
    }
    
    public boolean equals( Object obj )
    {
        // this test handles null and same class
        if( !super.equals( obj ) )
            return false;
        
        Student other = (Student) obj;
        return getID( ) == other.getID( );
    }
    
    public int hashCode( )
    {
        return super.hashCode( ) ^ id;
    }
        
    public String toString( )
    {
        return super.toString( ) + " " + getID( );
    }
    
    public Object clone( ) throws CloneNotSupportedException
    {
        Object copy = super.clone( );
        ((Student)copy).id = id;   // not really needed
        
        //Student copy = new Student( getName( ), getID( ) ); // WRONG!!
        return copy;
    }
    private int id;
}

class Undergrad extends Student
{
    public Undergrad( String nm, int id )
    {
        super( nm, id );
    }
}

class Faculty extends Person
{
    public Faculty( String name )
    {
        super( name );
    }
}

class ObjectDemo
{
    public static void main( String[] args )
    {
        Person s1 = new Student( "Jane Smith", 1234 );
        Person s2 = new Faculty( "Jane Smith" );
        Person s3 = new Student( "Jane Smith", 1234 );
        Person s4 = new Undergrad( "Jane Smith", 1234 );
        
        
        System.out.println( "s1: " + s1 + " " + s1.hashCode( ) );
        System.out.println( "s2: " + s2 + " " + s2.hashCode( ) );
        System.out.println( "s3: " + s3 + " " + s3.hashCode( ) );
        System.out.println( "s4: " + s4 + " " + s4.hashCode( ) );
        
        System.out.println( "s1.equals(s2): " + s1.equals( s2 ) );
        System.out.println( "s1.equals(s3): " + s1.equals( s3 ) );
        System.out.println( "s1.equals(s4): " + s1.equals( s4 ) );
        
        try
        {
            Person s5 = (Person) s4.clone( );
            System.out.println( "s5 (clone of s4) " + s5 + " " + s5.hashCode( ) );
            System.out.println( "s4.equals(s5): " + s4.equals( s5 ) );
        }
        catch( CloneNotSupportedException e )
        {
            System.out.println( "Oops... can't clone" );
        }
    }
}
