具体类型的Java包装类子类

时间:2014-12-17 19:17:19

标签: java subclass wrapper

我们说我有一个班级成员如下:

public class Person {
   String name;
   int age;
}

和许多子类,如

public class Student extends Person {
   // extra fields and methods
}
public class Teacher extends Person {
   // extra fields and methods
}

现在,考虑到对于某些应用程序,我需要为每个人实例分配一个整数id,但我不想扩展Person接口以在那里添加getId()和一个字段到持有身份证。一个简单的解决方案是使用类似的包装器:

public class PersonWrapper extends Person {
    public PersonWrapper(Person p, int id) { // assign the id and other fields }
    public int getId() { return id; }
}

这样客户端代码仍然可以使用Person接口,并且可以使用包装人员 作为一个人对待。 这种方法的问题是PersonWrapper是Person的子类,而不是教师或学生,这样的代码不会起作用:

Teacher t = new PersonWrapper(teacher, 1);
t.giveGrade();

当然,可以为Person的所有子类创建具体的包装类型,但我想知道是否有更优雅的解决方案。理想的解决方案是这样的:

public class PersonWrapper<T extends Person> extends T

这样任何PersonWrapper都是它包装类型的子类,但在Java和I中它是不可能的 怀疑这种定义可能无法用任何语言表达。

在任何情况下,如何在不更改与person及其子类一起使用的客户端代码的情况下将id分配给子类,而不为每个子类创建具体的包装器?

4 个答案:

答案 0 :(得分:4)

包装器不一定需要扩展到它包装的类。所以,只需使用PersonWrapper<T extends Person>

public class PersonWrapper<T extends Person> {
    T person;
    int id;

    public PersonWrapper(T person, int id) {
        this.person = person;
        this.id = id;
    }
    //getters and setters...
}

此外,类只能在编译时从另一个类扩展,因此PersonWrapper不能同时从StudentTeacher扩展,这使得不可能你正在寻找的东西。

唯一的解决方案是使用像cglib这样的库动态创建代理类。例如,当需要动态添加功能时,Spring会为类创建代理,例如为方法或全班添加事务管理。

答案 1 :(得分:1)

此问题的常见解决方案是将Person设为interface

interface Person {

    public String getName();

    public int getAge();
}

class ActualPerson implements Person {

    private final String name;
    private final int age;

    ActualPerson(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public int getAge() {
        return age;
    }
}

class PersonWithId implements Person {

    private final Person person;
    private final int id;

    PersonWithId(Person person, int id) {
        this.person = person;
        this.id = id;
    }

    @Override
    public String getName() {
        return person.getName();
    }

    @Override
    public int getAge() {
        return person.getAge();
    }
}
  

不要害怕大量的代码 - 与你后悔的时间相比,你编写代码的时间是微不足道的。 Old Curmudgeon 2014

答案 2 :(得分:0)

你是对的,你不能做你想做的事。假设您不能将具体类更改为Student extends Person implements Identifiable,那么最好的办法是将您的包装器视为包装器,并使用一个返回其不同元素的getter:

public class Wrapper<T> {
    private final T item;
    private final int id;

    ...

    public int getId() { return id }

    public T getItem() { return item; }

}

这是一个很难使用的,因为您必须执行wrapper.getItem().giveGrade()而不是wrapper.giveGrade()之类的操作。这也意味着你无法将包装器推入List<Teacher>,然后将其向下转发到TeacherWrapper - 但这有点脆弱,并且通常有更好的方法来实现你想要的。对于大多数情况,这种“纯粹”的包装方法可以满足您的需求。

请注意,我甚至没有T extends Person。如果包装类不需要使用任何Person方法,那么通过人为限制通用就无法获得多少收益。呼叫站点都将受到限制。 一个的区别在于,如果呼叫网站有Wrapper<?>,那么我的代码只会让您将该项目作为Object,而限制性更强T extends Person将允许您将该项目设为Person

答案 3 :(得分:0)

我希望我没有错过任何东西,但在我看来,包装模式解决了你的问题:

public class Person implements IPerson{
    String name;
    int age;

    public  static void main(String[] args)
    {
        Teacher teacherWithID = new Teacher(new PersonWithID(new Person()));
        Teacher teacherWithoutID = new Teacher(new Person());
    }
}

interface IPerson{}

class Teacher implements IPerson{
    public Teacher(IPerson personToBeWrapped){}
}

class Student implements IPerson{
    public Student(IPerson personToBeWrapped){}
}

class PersonWithID implements IPerson{
    public PersonWithID(IPerson personToBeWrapped){}
}

你的变量的类型应该是最后一个包装器。

包装器模式可以被认为是一种机制,允许您在运行时“扩展”类。由于这个原因,它也被称为装饰器。您的代码中有竞争继承机制。 (内置的一个和模式)结果是你不能输入你的变量。 如果你只使用模式,它就可以工作。