Extending base class containing abstract generic method in java

15,654

Solution 1

If you cannot modify the base class, there isn't really a way to do it that involves generics. There are two things you could do. One, check the run-time type of the object and do one thing if it is a teacher, another if not.

public class MyClass extends MyBase {
    @Override public <T> Student assign(T object) {
        Student student = new Student();
        if (object instanceof Teacher) {
            Teacher teacher = (Teacher) object;
            student.setTeacherName(teacher.getName());
        } else {
            student.setTeacherName("unknown");
        }
        return student;
    }
}

Two, create two separate assign methods in MyClass, one which overrides the generic method in the base class, and a non-generic method that just takes a Teacher as a parameter.

public class MyClass extends MyBase {
    @Override public <T> Student assign(T object) {
        return new Student();
    }

    public Student assign(Teacher teacher) {
        Student student = new Student();
        student.setTeacherName(teacher.getName());
        return student;
    }
}

When the method is called, if the compile-time type is Teacher, the compiler will choose the more specific overload of the method:

public class Main {
    /**
     * Gets a teacher but calls it an Object.
     *
     * @return an Object that happens to be a Teacher
     */
    public static Object getTeacher() {
        return new Teacher();
    }

    public static void main(String[] args) {
        /** A Teacher, and is declared as such */
        Teacher teacher = new Teacher();

        /**
         * Some Object, which just so happens to actually
         * be a Teacher (but the compiler can't see that)
         */
        Object something = getTeacher();

        MyClass mc = new MyClass();

        Student withTeacher = mc.assign(teacher);
        Student withoutTeacher = mc.assign(something);
    }
}

However, I would much prefer one of the solutions that modifies MyBase.

Edited to add: You could actually declare the second, more specific assign method to be a generic method, like so:

public <T extends Teacher> Student assign(T object) {
    Student student = new Student();
    student.setTeacherName(object.getName());
    return student;
}

But this is really just the same as the second example above. The method does not override the method in the base class, it still relies on the compiler picking the more specific overload at compile time, and the erasure of the method signature is exactly the same as the non-generic method in the second example:

public Student assign(Teacher)

It just looks a bit more like an override of the method from the base class because it's got a generic type variable and the parameter is named object instead of teacher. But try sticking an @Override annotation on it and the compiler will tell you it does not override or implement a method from a supertype.

And you still have the <T> generic method hanging around which doesn't set the teacher name, and if you ever have a Teacher object which is not declared as such, the compiler will choose the wrong overload and you'll be scratching your head wondering why, if the object is a Teacher, the teacher name didn't get set on the student.

Solution 2

You cannot guarantee that the object being passed in to MyClass's assign method is a Teacher, if MyBase is defined this way. If you are allowed to modify MyBase, do the following:

abstract class MyBase
{
   public abstract <T extends Teacher> Student assign (T object);
}

and extend it with your class:

class MyClass extends MyBase
{
   @Override
   public <T extends Teacher> Student assign (T object){

     Student stew = new Student();
     ......

   }
}

This will guarantee that the object being passed in is a Teacher, even if it's really an instance of a subclass of Teacher.

EDIT: Another way to modify MyBase. Make the generic parameter part of the class definition instead of the method definition.

abstract class MyBase<T extends Teacher>
{
   public abstract Student assign (T object);
}

class MyClass<T extends Teacher> extends MyBase<T>
{
   @Override
   public  Student  assign (T object)
   {
      Student stew = new Student();
      ......
   }
}

// Could also do this...
class MyClass2 extends MyBase<Teacher>
{
   @Override
   public  Student  assign (Teacher object)
   {
      Student stew = new Student();
      ......
   }
 }

Here's Oracle's Java Generics Tutorial.

Solution 3

Generic methods are usually meant to work with any type supplied, as opposed to generic classes, which can be specialised to work with concrete types. It's a functional vs OO approach. So what you probably want is to have a generic class with a T parameter:

MyBase<T>

have the method use that parameter

public abstract Student assign (T object)

and then specialise it to use teachers

MyClass extends MyBase<Teacher>

with a

public Student assign (Teacher object)

Mostly my generic methods are static for exactly this reason - if an instance method is parametrised, it is logical that the type parameter belongs to the class rather than the method. Usually, of course.

Share:
15,654
chapstick
Author by

chapstick

Updated on June 04, 2022

Comments

  • chapstick
    chapstick almost 2 years

    I am given a Base class like this:

     Class MyBase{
    
       public abstract <T> Student assign (T object);
    
     }
    

    I am extending it like below:

     Class MyClass extends MyBase{
    
       @Override
       public abstract <T> Student assign (T object){
    
         Student stew = new Student();
         ......
    
       }
     }
    

    My intended use is: the passed in object should be of type Teacher. Within the method I want to create a new Student which will get some values by invoking functions on Teacher. (For example, stew.setTeacherName = teacher.getName();) And then return Student to the caller.

    Questions: a) How do I get the intended behavior? b) Whats the difference between declaring MyBase the current way vs. doing MyBase ? c) I am interested in knowing the solution in case where I CAN change Base class and I CANNOT change the Base class.

    Thanks in advance for your help. Also, if there are any useful resources/tutorials you can point me to that would be great. Thanks again.

  • chapstick
    chapstick about 11 years
    thanks for the response. But what if I cannot modify the Base class?