【问题标题】:Can Java Generics replace multiple similar classes?Java 泛型可以替换多个相似的类吗?
【发布时间】:2014-05-27 17:26:37
【问题描述】:

问题

我正在尝试使用 Java 泛型来替换具有类似方法的类。我发现的所有示例都包含简单示例,但我不确定 Java 泛型是否打算以这种方式使用。

我有 2 个父类和 2 个子类,它们的方法几乎相同。两个父类也派生自不同的类。最终,我希望能够使用一段代码来创建和操作其中一个父类,然后是它的子类,而不需要大量的 switch 语句或其他具有重复代码的流控制。

这就是我的想法,尽管我还没有能够让它以这种方式工作,无论是语法还是不是泛型的功能。

父类

public class FooParent
{
    private FooChild fooChild;
    public FooChild getChild()
    {
        return fooChild;
    }
}

public class BarParent
{
    private BarChild barChild;
    public BarChild getChild()
    {
        return barChild;
    }
}

儿童班

public class FooChild
{
    public void print()
    {
        System.out.println("I'm a foo child");
    }
}

public class BarChild
{
    public void print()
    {
        System.out.println("I'm a bar child");
    }
}

泛型类

public class GenericParent<T>
{
    private T self;
    public GenericParent(T self)
    {
        this.self = self;
    }

    public GenericChild getChild()
    {
        return new GenericChild(self.getChild());
    }
}

public class GenericChild<T>
{
    private T self;
    public GenericChild(T self)
    {
        this.self = self;
    }

    public void print()
    {
        self.print();
    }
}

我如何想要使用它们

public static void main(String args[])
{
    GenericParent parent;

    // Only the initialization of the parent variable needs specialized code
    switch(args[0])
    {
        case "foo":
            parent = new GenericParent(new FooParent());
        break;

        case "bar":
            parent = new GenericParent(new BarParent());
        break;
    }

    // From here on out, it's all generic
    parent.getChild().print();
}

用法和期望的输出

java genericExample foo
> I'm a foo child

java genericExample bar
> I'm a bar child

最后的问题

也许“子”和“父”用词不当,因为我知道它们实际上并没有被继承,但最重要的是,一个类使用某些方法返回它的“子”。所以这是一个问题的很多代码,实际上可能无法通过这种方式解决,但希望你能回答我这个问题:

  1. 这是 Java 泛型可以完成的事情吗?
  2. 如果没有,Java 中有解决此问题的方法吗?

谢谢!

编辑

我无法编辑“Foo”和“Bar”类。我的最终问题是:我可以在不使用公共父类的情况下将任一类的一个实例存储在单个变量中吗?

【问题讨论】:

  • 您是否考虑过使用反射来触发父母的 .getChild() 和孩子的 .print()?
  • 另一个可能更简洁的选择是让父母和孩子遵守一个接口,该接口公开您在 main 中需要的方法。
  • 您的实际问题是什么? Foo 和 Bar 并不能真正判断是否有一个好的解决方案,或者您是否只是在尝试滥用泛型。
  • 你实际上想实现什么?也许 OOP 不是最适合您的问题?
  • “实际问题”是我有两个类,如 Foo 和 Bar。这两个课程都是必要的。这些类不是我写的,我只能照原样使用它们。初始化后,使用任一类的实例是相同的,所以我需要的只是将任一类实例存储到可以使用的单个变量中,这样我就不需要一堆 switch 语句和重复代码。跨度>

标签: java class generics capability generalization


【解决方案1】:

我认为你想要的是多态性,而不是泛型:

public class test {
    public class FooParent implements hasPrintableChildren
    {
        private FooChild fooChild;
        public FooChild getChild()
        {
            return fooChild;
        }
    }

    public class BarParent implements hasPrintableChildren
    {
        private BarChild barChild;
        public BarChild getChild()
        {
            return barChild;
        }
    }


    public class FooChild implements canPrint
    {
        public void print()
        {
            System.out.println("I'm a foo child");
        }
    }

    public class BarChild implements canPrint
    {
        public void print()
        {
            System.out.println("I'm a bar child");
        }
    }

    public interface hasPrintableChildren{
        public canPrint getChild();
    }
    public interface canPrint{
        public void print();
    }



    public static void main(String args[])
    {
        hasPrintableChildren parent;
        // Only the initialization of the parent variable needs specialized code
        switch(args[0])
        {
            case "foo":
                parent = new FooParent();
            break;

            case "bar":
                parent = new BarParent();
            break;
        }

        // From here on out, it's all generic
        parent.getChild().print();
    }
}

OP 澄清说他会对反射选项感兴趣:

    public static void main(String args[]) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException
    {
        Object parent;
        // Only the initialization of the parent variable needs specialized code
        switch(args[0])
        {
            case "foo":
                parent = new FooParent();
            break;

            case "bar":
                parent = new BarParent();
            break;
        }

        // From here on out, it's all generic
        Object child = parent.getClass().getMethod("getChild").invoke(parent);
        child.getClass().getMethod("print").invoke(child);
    }

注意:我不推荐这种硬编码反射。像这样的代码通常会带来更大的设计问题。

【讨论】:

    【解决方案2】:

    你的父母似乎是一个包装器,包装器是一个容器,所以是的它可能是可以从类型参数中受益的东西。

    但除了构造函数签名之外,我看不到任何类型参数(self 是什么?没有边界,没有提示,没有任何东西......),所以使用泛型类型 这里不给你买任何东西。如果你感兴趣的方法返回 void 并声明一个空参数列表,那么引入类型参数是没有用的。

    这里是指导:如果你的类中的方法会受益于有一个类型参数,即如果一个类型参数在任何方法返回类型或签名中有用,那么泛化你的类。否则,坚持你目前拥有的。

    【讨论】:

      【解决方案3】:

      没有。这通常不是你会使用泛型的东西,而是你会使用接口或抽象类的东西,在你的例子中,可能是匿名内部类。

      这是一个几乎可以说明所有内容的示例:

      Main.java

      public class Main {
          public static void main(String args[])
          {
              AbstractParent parent;
              // Only the initialization of the parent variable needs specialized code
              switch(args[0])
              {
                  case "foo":
                      parent = new FooParent();
                      break;
      
                  default:
                      parent = new BarParent();
                      break;
              }
      
              // From here on out, it's all generic
              parent.getChild().print();
          }
      }
      

      Child.java

      public interface Child {
          void print();
      }
      

      AbstractParent.java

      public abstract class AbstractParent {
          protected Child child;
      
          public Child getChild() {
              return child;
          }
      }
      

      BarParent.java

      public class BarParent extends AbstractParent {
          public BarParent() {
              child = new Child() {
                  @Override
                  public void print() {
                      System.out.println("I'm a bar child");
                  }
              };
          }
      }
      

      FooParent.java

      public class FooParent extends AbstractParent {
          public FooParent() {
              child = new Child() {
                  @Override
                  public void print() {
                      System.out.println("I'm a foo child");
                  }
              };
          }
      }
      

      借助 Java 8 中的一些新语言特性,您可以做更酷的事情。但是,让我们再谈一谈吧。

      【讨论】:

        【解决方案4】:

        是的,泛型和多态性可以帮助您:

        public class Foo {} // declared elsewhere
        
        
        public class Bar {} // declared elsewhere
        
        
        public abstract class GenericParent<T> {
            private T self;
        
            public GenericParent(T self) {
                this.self = self;
            }
        
            protected T getSelf() {
                return self;
            }
        
            public abstract GenericChild<T> getChild();
        }
        
        
        public class FooChild extends GenericChild<Foo> {
            public FooChild(Foo foo) {
                super(foo);
            }
        }
        
        
        public class BarChild extends GenericChild<Bar> {
            public BarChild(Bar bar) {
                super(bar);
            }
        }
        
        
        public class FooParent extends GenericParent<Foo> {
        
            public FooParent(Foo foo) {
                super(foo);
            }
        
            public FooParent() {
                this(new Foo()); 
            }
        
            @Override
            public GenericChild<Foo> getChild() {
                return new FooChild(getSelf());
            }
        }
        
        
        public class BarParent extends GenericParent<Bar> {
        
            public BarParent(Bar bar) {
                super(bar);
            }
        
            public BarParent() {
                this(new Bar());
            }
        
            @Override
            public GenericChild<Bar> getChild() {
                return new BarChild(getSelf());
            }
        }
        

        你还必须稍微改变你的 main 方法:

        public static void main(String args[]) {
            GenericParent<?> parent;
        
            // Only the initialization of the parent variable needs specialized code
            switch(args[0]) {
                case "foo":
                    parent = new FooParent();
                    break;
        
                case "bar":
                    parent = new BarParent();
                    break;
            }
        
            parent.getChild().print();
        }
        

        【讨论】:

        • 感谢您的回答。我明白那样做会有什么意义。不幸的是,我无法修改我拥有的“Foo”和“Bar”类。我坚持他们的方式。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-06-15
        • 1970-01-01
        • 1970-01-01
        • 2013-06-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多