代码之家  ›  专栏  ›  技术社区  ›  Matt Mitchell

如何编写一个Java函数,它返回一个“这个”的类型化的实例,并在扩展时工作?

  •  2
  • Matt Mitchell  · 技术社区  · 16 年前

    这是一个关于LazyWeb的问题,但是你得到了代表,所以:—)

    我有一个Java类,它返回自己的实例以允许链接。 (例如classobject.dostuff().dostuff())

    例如:

    public class Chainer
    {
        public Chainer doStuff()
        {
           /* Do stuff ... */
           return this;
        }
    }
    

    我想延长这门课。是否有一种方法(可能使用泛型)来扩展此类而不必覆盖每个方法签名?

    例如:

    public class ChainerExtender extends Chainer
    {
        public ChainerExtender doStuff()
        {
           super.doStuff();
           return this;
        }
    }
    

    我已经尝试过:

    public class Chainer
    {
        public <A extends Chainer> A doStuff()
        {
           /* Do stuff ... */
           return (A)this;
        }
    }
    
    public class ChainerExtender extends Chainer
    {
        public <A extends Chainer> A doStuff()
        {
           /* Do stuff ... */
           return super.doStuff();
        }
    }
    

    但这并不能产生错误:

    type parameters of <A>A cannot be determined; 
    no unique maximal instance exists for type variable A with upper bounds A,Chainer
    

    我是否必须有类声明,比如:

    public class Chainer<T extends Chainer<T>> {}
    public class ChainerExtender extends Chainer<ChainerExtender>
    

    按照 this question ?

    7 回复  |  直到 11 年前
        1
  •  9
  •   Dov Wasserman    16 年前

    你试过直击吗

    public class Chainer
    {
        public Chainer doStuff()
        {
           /* Do stuff ... */
           return this;
        }
    }
    
    public class ChainerExtender extends Chainer
    {
        @Override
        public ChainerExtender doStuff()
        {
           /* Do stuff ... */
           super.doStuff();
           return this;
        }
    }
    

    使用Java 5,您可以声明具有共变返回类型的重写方法,这就意味着这一点:子类可以具有更具体的返回类型的重写方法签名。

        2
  •  6
  •   tvanfosson    16 年前

    为什么不让他们都返回一个接口?您必须在接口中定义所有可能的链接方法,并在基类中提供“空”实现,但是您可以链接到您的核心内容。

    public interface IChainer
    {
      IChainer doStuff();
      IChainer doSomethingElse();
    }
    
    public class Chainer implements IChainer
    {
      public IChainer doStuff()
      {
         // really do something
         return this;
      }
    
      public IChainer doSomethingElse()
      {
          return this; // do nothing
      }
    }
    
    public class ChainerExtender extends Chainer
    {
       // simply inherits implementation for doStuff()
    
       public override IChainer doSomethingElse()
       {
           // really do something
           return this;
       }
    }
    

    注意:上面可能有一些语法问题。过去几年,我主要是用C语言编程的。希望你能理解。

        3
  •  2
  •   bhavanki    16 年前

    我让它这样工作。它使用一个类文本来表示在运行时要强制转换到什么类型(参见第8节 Gilad Bracha's Generics Tutorial )

    public class Chainer {
      public <T extends Chainer> T doStuff (Class<T> foo) {
        /* do stuff */
        return foo.cast (this);
      }
    }
    
    public class ChainerExtender extends Chainer {
      public <T extends ChainerExtender> doOtherStuff (Class<T> foo) {
        /* do other stuff */
        return foo.cast (this);
      }
    }
    

    所以电话是这样的:

    Chainer c1 = new Chainer();
    c1 = c1.doStuff (c1.getClass());
    // ...
    ChainerExtender ce1 = new ChainerExtender();
    ce1 = ce1.doStuff (ce1.getClass()).doOtherStuff (ce1.getClass());
    

    这种技术的一个缺点是,如果你有 Chainer 引用,你必须明确检查它是否是 ChainerExtender 调用前的对象 doOtherStuff 关于它。但是,这是有意义的,因为尝试在 链环 对象在运行时将产生错误。

        4
  •  1
  •   VonC    16 年前

    你能不能发布一个完整的例子来说明你看到的错误信息?

    我只是编译并执行了以下内容,没有任何障碍:

    public class Test {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            ChainerExtender c = new ChainerExtender();
            c.doStuff().doStuff();
        }
    
        public static class Chainer
        {
            public <A extends Chainer> A doStuff()
            {
               /* Do stuff ... */
                System.out.println("Chainer");
               return (A)this;
            }
        }
    
        public static  class ChainerExtender extends Chainer
        {
            /** 
             * @see test.Test.Chainer#doStuff()
             */
            @Override
            public <A extends Chainer> A doStuff()
            {
                System.out.println("ChainerExtender");
               return super.doStuff();
            }
        }
    
    }
    

    给予:

    ChainerExtender
    Chainer
    ChainerExtender
    Chainer
    

    我误解了这个问题,请投反对票并发表评论。

        5
  •  1
  •   user21714    16 年前

    自从我做过任何Java泛型以来,已经有一段时间了。

    如何:

    <T>
    public class  Chainer 
    {
        public T doStuf()
       {
       }
    
    }
    
    public class ChainerDerived : extends Chainer<ChainerDerived>
    {
       public ChainerDerived doStuf()
       {
       }
    }
    
    

    我最近大部分都在做C++,这类东西只是课程的标准。Java泛型支持这种类型的结构吗?

    我希望我理解你的问题。

        6
  •  1
  •   Ignacio Coloma    16 年前

    请注意,“super.<a>dostuff()”将编译 工作。它提示了您所期望的类型的泛型。

        7
  •  1
  •   JodaStephen    11 年前

    我认为简单的答案是“不,你不能”。我知道这可能是多么有用,但我觉得继承的使用对我来说是“意想不到的后果”。一个人决定在子类中添加一些额外的功能只是时间问题,而不仅仅是 super.doStuff() 控制流程将非常难以遵循。

    一个可能的解决方案是为您的链子(如果他们使用 建设者 模式)返回不同生成器类的新实例,而不是 this . 看看史蒂芬·科尔伯恩在年对日期创建者做了什么。 JSR 310 从API的角度来看,这是相当聪明的,尽管写起来很笨拙。