如何将java泛型转换为Delphi

时间:2013-10-03 13:39:08

标签: java delphi generics delphi-xe2

我正在将Nat Pryce的Maybe课程翻译成德尔福 这是一些背景:http://www.natpryce.com/articles/000776.html

大部分都很容易,但是......
我如何将这个声明从Java翻译成Delphi?

public abstract <U> Maybe<U> to(Function<? super T, ? extends U> mapping);

这是一个抽象的虚函数&to,它将function命名为mapping作为参数,但我不确定如何对此进行建模。

对于上下文,这是完整的代码:

package com.natpryce.maybe;

import com.google.common.base.Function;
import com.google.common.base.Predicate;

import java.util.Collections;
import java.util.Iterator;

public abstract class Maybe<T> implements Iterable<T> {
    public abstract boolean isKnown();
    public abstract T otherwise(T defaultValue);
    public abstract Maybe<T> otherwise(Maybe<T> maybeDefaultValue);
    public abstract <U> Maybe<U> to(Function<? super T, ? extends U> mapping);
    public abstract Maybe<Boolean> query(Predicate<? super T> mapping);

    public static <T> Maybe<T> unknown() {
        return new Maybe<T>() {
            @Override
            public boolean isKnown() {
                return false;
            }

            public Iterator<T> iterator() {
                return Collections.<T>emptyList().iterator();
            }

            @Override
            public T otherwise(T defaultValue) {
                return defaultValue;
            }

            @Override
            public Maybe<T> otherwise(Maybe<T> maybeDefaultValue) {
                return maybeDefaultValue;
            }

            @Override
            public <U> Maybe<U> to(Function<? super T, ? extends U> mapping) {
                return unknown();
            }

            @Override
            public Maybe<Boolean> query(Predicate<? super T> mapping) {
                return unknown();
            }

            @Override
            public String toString() {
                return "unknown";
            }

            @Override
            @SuppressWarnings({"EqualsWhichDoesntCheckParameterClass"})
            public boolean equals(Object obj) {
                return false;
            }

            @Override
            public int hashCode() {
                return 0;
            }
        };
    }

    public static <T> Maybe<T> definitely(final T theValue) {
        return new DefiniteValue<T>(theValue);
    }

    private static class DefiniteValue<T> extends Maybe<T> {
        private final T theValue;

        public DefiniteValue(T theValue) {
            this.theValue = theValue;
        }

        @Override
        public boolean isKnown() {
            return true;
        }

        public Iterator<T> iterator() {
            return Collections.singleton(theValue).iterator();
        }

        @Override
        public T otherwise(T defaultValue) {
            return theValue;
        }

        @Override
        public Maybe<T> otherwise(Maybe<T> maybeDefaultValue) {
            return this;
        }

        @Override
        public <U> Maybe<U> to(Function<? super T, ? extends U> mapping) {
            return definitely(mapping.apply(theValue));
        }

        @Override
        public Maybe<Boolean> query(Predicate<? super T> mapping) {
            return definitely(mapping.apply(theValue));
        }

        @Override
        public String toString() {
            return "definitely " + theValue.toString();
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            DefiniteValue<?> that = (DefiniteValue<?>) o;

            return theValue.equals(that.theValue);

        }

        @Override
        public int hashCode() {
            return theValue.hashCode();
        }
    }
}

2 个答案:

答案 0 :(得分:5)

Java代码利用参数类型中的协方差和逆变。 to的参数应该是一个带有一个参数的函数。参数的类型可以是T 或任何超类型T 。该函数应返回U 类型的值或U 的任何子类型。

to函数将调用mapping并将其当前保存的类型T的值传递给它(如果它根本保存任何值),因此参数类型为mapping需要是T可以分配的内容。这包括TObject以及层次结构中它们之间的其他类型。

同样,to将使用mapping的结果来获取类型U的值。 Mapping不必声明为具体返回U,但无论何种类型返回都需要赋予U

在Delphi中无法表达这些约束。您可以做的最好的事情是请求一个使用字面TU的函数类型:

function &to<U>(mapping: TFunc<T, U>): Maybe<U>;

可能能够通过引入另一个类型参数来表达对U的约束:

function &to<U: class; V: U>(mapping: TFunc<T, V>): Maybe<U>;

我不知道类型参数是否可以用作同一参数列表中其他参数的约束。我也不确定编译器是否可以在该上下文中推断出V的值,因此调用to会很麻烦。

另一方面,如果虚方法不能有新的通用参数(如U),那么约束根本不重要,因为你无法声明方法开始。

但是,根据参考实现,Maybe只有两个具体实现,因此有一种可能性是将to实现为非虚拟函数,然后手动检查运行时类型。它不会很漂亮。像这样:

if Self is TUnknown<T> then
  Result := unknown<U>
else
  Result := definitely(mapping(TDefiniteValue<T>(Self).value));

答案 1 :(得分:2)

好吧,让我们忽略开始时的约束。 Java类型Function<T,R>映射到Delphi中的TFunc<T,R>。除此之外,函数toU类型上是通用的,但是Maybe<T>的方法。这导致我们:

type
  Maybe<T> = class
    function &to<U>(mapping: TFunc<T, U>): Maybe<U>; virtual; abstract;
  end;

不幸的是,这不会编译。编译器发出 E2533虚拟,动态和消息方法不能有类型参数这对我来说是一个新的参数。这令人失望。

更新:正如Rob指出的那样,当他的答案出现时,我还没有考虑约束,这些约束都不能在Delphi中表达。