Annie W.
Annie W.

Reputation: 438

Java: use generics as abstraction layer

I'm trying to use generics as abstraction layer similar as Java collections. Here is a simplified example: the class EmployeeRecord stores information about employee and class Table should be generic and able to store various type of data. The type is passed to Table as generics. I've problem with pass calls to the stored, specific class.

What's wrong with call of method print()? How do I solve it?

class EmployeeRecord
{
  String name;

  EmployeeRecord( String name )
  {
    this.name = name;
  }

  void print()
  {
    System.out.println( name );
  }
} 

class Table<Record>
{
  Record rec;

  void set( Record rec )
  {
    this.rec = rec;
  }

  void printAll()
  {
    rec.print(); // COMPILER ERROR
/*
Test.java:27: error: cannot find symbol
    rec.print();
       ^
  symbol:   method print()
  location: variable rec of type Record
  where Record is a type-variable:
    Record extends Object declared in class Table
1 error
*/
  }
} 

public class Test
{
  public static void main( String[] argv )
  { 
    EmployeeRecord emp = new EmployeeRecord("John");
    Table<EmployeeRecord> tab = new Table<EmployeeRecord>();
    tab.set( emp );
    tab.printAll();
  }
}

Upvotes: 0

Views: 265

Answers (2)

Sylvain Leroux
Sylvain Leroux

Reputation: 51990

Java compiles generics using type erasure. If the type is not bound, the compiler treats instances of the parameterized type as being an Object instance.

To solve that, you have to bind the type to some super-type having the desired method:

interface Record {
    ...
}

class Table<T extends Record> {
    ...
}

Once bound, the compiler know that you can call any method of Record on an instance of type T.

In addition, it will reject any attempt to instantiate a Table with a parameterized type that is not a sub-class of Record. Whereas, in your original code, your Table object may be instantiated with any type parameter (which was probably not the desired behavior).

Upvotes: 0

Adi
Adi

Reputation: 2394

One way to achieve this is to create common interface which all your record classes is going to implement

interface Record{
    void print();
}

Then your EmployeeRecord class will look like this

class EmployeeRecord implements Record
{
    String name;

    EmployeeRecord( String name )
    {
       this.name = name;
    }

    @Override
    public void print()
    {
        System.out.println( name );
    }
}

And your Table will look like this

class Table<T extends Record>
{
    T rec;

    void set( T rec )
    {
        this.rec = rec;
    }

    void printAll()
    {
       rec.print(); 
    }
}

Then you call this from main method like this

public class Test {

    public static void main(String[] args) {
         EmployeeRecord emp = new EmployeeRecord("John");
         Table<EmployeeRecord> tab = new Table<EmployeeRecord>();
         tab.set( emp );
         tab.printAll();
    }
}

Upvotes: 1

Related Questions