36

我最近刚听说过鸭子类型,并且阅读了有关它的Wikipedia 文章,但是我很难将这些示例翻译成 Java,这确实有助于我的理解。

任何人都能够给出一个在 Java 中进行鸭式输入的清晰示例以及我可能如何使用它?

4

10 回答 10

48

Java 在设计上不适合鸭子类型。您可能选择这样做的方式是反射:

public void doSomething(Object obj) throws Exception {

    obj.getClass().getMethod("getName", new Class<?>[] {}).invoke(obj);
}

但我主张用动态语言来做,比如 Groovy,这样更有意义:

class Duck {
    quack() { println "I am a Duck" }
}

class Frog {
    quack() { println "I am a Frog" }
}

quackers = [ new Duck(), new Frog() ]
for (q in quackers) {
    q.quack()
}

参考

于 2009-07-03T15:11:59.573 回答
11

请参阅此博客文章。它非常详细地说明了如何使用动态代理在 Java 中实现鸭子类型。

总之:

  • 通过鸭子类型创建一个表示您要使用的方法的接口
  • 创建一个使用此接口的动态代理和一个通过反射调用底层对象上的接口方法的实现对象(假设签名匹配)
于 2010-05-16T08:57:21.637 回答
7

检查这个库

interface MyInterface {
    void foo();
    int bar(int x, int y);
    int baz(int x);
}

public class Delegate {
    public int bar() {
        return 42;
    }
}

DuckPrxy duckProxy = new DuckPrxyImpl();
MyInterface prxy = duckProxy.makeProxy(MyInterface.class, new Delegate());
prxy.bar(2, 3); // Will return 42.

使用动态代理的接口鸭子类型很简单,您应该匹配方法名称和返回类型。

于 2009-07-03T15:11:10.310 回答
6

Java 没有实现鸭子类型。

于 2009-07-03T15:12:03.397 回答
3

使用 java 8,您有两种方法:

nº1:如果您只需要一种方法,请使用 lambdas

static interface Action { public int act(); }

public int forEachAct(List<Action> actionlist) {
   int total = 0;
   for (Action a : actionList)
       total += a.act();
}

public void example() {
    List<Action> actionList = new ArrayList<>();
    String example = "example";
    actionList.add(example::length);
    forEachAct(actionList);
}

nº2:使用匿名类(在性能方面不是很明智,但在一些非关键部分可以做到)

static interface Action {
    public int act();
    public String describe();
}

public void example() {
    List<Action> actionList = new ArrayList<>();
    String example = "example";

    actionList.add(new Action(){
        public int act() { return example.length(); }
        public String describe() { return "Action: " + example; }
    });
}
于 2014-09-19T18:13:30.820 回答
2

我编写了一个实用程序类来为对象动态创建装饰器。您可以将其用于鸭子打字: https ://gist.github.com/stijnvanbael/5965616

例子:

interface Quacking {
    void quack();
}

class Duck {
    public void quack() { System.out.println("Quack!"); }
}

class Frog {
    public void quack() { System.out.println("Ribbip!"); }
}

Quacking duck = Extenter.extend(new Duck()).as(Quacking.class);
Quacking frog = Extenter.extend(new Frog()).as(Quacking.class);

duck.quack();
frog.quack();

输出:

Quack!
Ribbip!
于 2017-10-16T15:57:01.503 回答
1

Java 中的输入是名义上的——兼容性是基于名称的。如果您需要 Java 中的鸭子类型(或结构类型)的示例,请查看此页面:http : //whiteoak.sourceforge.net/#Examples,它提供了用 Whiteoak 编写的程序示例:A Java-也支持结构类型的兼容语言。

于 2009-07-03T15:17:31.183 回答
1

通常,duck 类型与动态类型语言一起使用。您将在运行时检查是否存在满足您的需求所需的方法或属性,而不管继承层次结构如何。

除了使用会变得丑陋的反射之外,您可以获得的最接近的方法是使用符合鸭子类型所需标准的最小接口。这篇文很好地描述了这个概念。它失去了在 python 或 ruby​​ 或 javascript 中输入鸭子的大部分简单性,但如果您正在寻找高水平的可重用性,它实际上在 Java 中是非常好的实践。

于 2009-07-03T15:19:02.397 回答
1

不错的定义:

对象是多态的,没有共同的基类或接口相关。

参考

于 2016-01-12T04:58:08.707 回答
1

聚会迟到了(像往常一样),但我写了一个快速课程来自己做一些鸭子打字。见这里

它只会转到接口,但对于一个使用示例:

interface Bird {
    void fly();
}

interface Duck extends Bird {
    void quack();
}

class PseudoDuck {
    void fly() {
        System.out.println("Flying");
    }

    void quack() {
        System.out.println("Quacking");
    }
}

class Tester {
    @Test
    void testDuckTyping() {
        final Duck duck
                = DuckTyper.duckType(new PseudoDuck(), Duck.class);
    }
}

支持默认接口方法,参数,检查异常类型是否兼容,会检查PseudoDuck类的所有方法(包括私有方法)。不过,尚未对通用接口进行任何测试。

于 2018-04-09T21:19:54.830 回答