2

我有一个这样的基类:

 Class MyBase{

   public abstract <T> Student assign (T object);

 }

我正在扩展它,如下所示:

 Class MyClass extends MyBase{

   @Override
   public abstract <T> Student assign (T object){

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

   }
 }

我的预期用途是:传入的对象应该是教师类型。在该方法中,我想创建一个新学生,它将通过调用教师上的函数来获取一些值。(例如,stew.setTeacherName = teacher.getName();)然后将Student返回给调用者。

问题: a) 我如何获得预期的行为?b) 以当前方式声明 MyBase 与执行 MyBase 有什么区别?c)如果我可以更改基类并且我不能更改基类,我有兴趣了解解决方案。

在此先感谢您的帮助。此外,如果有任何有用的资源/教程,你可以指出我会很棒。再次感谢。

4

3 回答 3

7

如果以这种方式定义,您不能保证object传入MyClass' 的assign方法是 a 。如果允许修改,请执行以下操作:TeacherMyBaseMyBase

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

并用你的类扩展它:

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

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

   }
}

这将保证object传入的是 a Teacher,即使它实际上是 的子类的实例Teacher

编辑:另一种修改方式MyBase。使泛型参数成为类定义的一部分,而不是方法定义。

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();
      ......
   }
 }

这是Oracle 的 Java 泛型教程

于 2013-02-19T23:13:35.810 回答
2

如果您无法修改基类,则实际上没有涉及泛型的方法。你可以做两件事。一,检查对象的运行时类型,如果是老师,则做一件事,如果不是,则做另一件事。

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;
    }
}

二,在 MyClass 中创建两个单独assign的方法,一个覆盖基类中的泛型方法,以及一个仅将 Teacher 作为参数的非泛型方法。

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;
    }
}

调用方法时,如果编译时类型为 Teacher,编译器会选择方法更具体的重载:

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);
    }
}

但是,我更喜欢修改 MyBase 的解决方案之一。

编辑添加:您实际上可以将第二个更具体assign的方法声明为通用方法,如下所示:

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

但这实际上与上面的第二个示例相同。该方法不会覆盖基类中的方法,它仍然依赖于编译器在编译时选择更具体的重载,并且方法签名的擦除与第二个示例中的非泛型方法完全相同:

public Student assign(Teacher)

它只是看起来更像是基类中方法的覆盖,因为它有一个泛型类型变量,并且参数被命名object而不是teacher. 但是尝试@Override在其上添加注释,编译器会告诉您它不会覆盖或实现超类型的方法。

而且你仍然有<T>没有设置教师名称的通用方法,如果你有一个没有这样声明的教师对象,编译器会选择错误的重载,你会摸不着头脑想知道为什么,如果对象是教师,则不会为学生设置教师名称。

于 2013-02-20T00:23:50.160 回答
1

泛型方法通常适用于提供的任何类型,而不是泛型类,后者可以专门用于处理具体类型。这是一种功能性与 OO 的方法。所以你可能想要的是有一个带有 T 参数的泛型类:

MyBase<T>

让方法使用该参数

public abstract Student assign (T object)

然后将其专门用于使用教师

MyClass extends MyBase<Teacher>

public Student assign (Teacher object)

大多数情况下,我的泛型方法都是静态的,正是因为这个原因——如果实例方法被参数化,那么类型参数属于类而不是方法是合乎逻辑的。通常,当然。

于 2013-02-19T23:12:49.213 回答