java中的函数“组合”和类型安全

时间:2013-11-08 12:06:43

标签: java generics types composition

我正在尝试实现一个隔离对象的一部分的类,并用其他东西替换该部分。对象的一部分可能与对象本身的类型不同。

一个简单的例子是一个接受字符串“--12--”的类,隔离ascii数字,并用下一个自然数替换它。因此,整个序列将是"--12--" -> "12" -> 12 -> 13 -> "13" -> "--13--"

考虑到这一点,我实施了以下内容:

public abstract class Replacer<Outer, Inner>
    {
    protected abstract Inner decompose(Outer something);
    protected abstract Outer compose(Inner something);
    protected abstract Inner inner_replace(Inner something);
    public Outer replace(Outer something)
        {
        Inner s = decompose(something);
        s = inner_replace(s);
        return compose(s);
        }
    }

现在,我希望能够编写一系列替换器 - 将它们堆叠起来,以便每个人使用“较低”的替换器计算其inner_replace

public abstract class ComposableReplacer<Outer, Inner> extends Replacer<Outer, Inner>
    {
    protected Replacer<Inner, ?> child;

    @Override
    public Outer replace(Outer something)
        {
        Inner s = decompose(something);
        s = inner_replace(s);
        if (child!=null)
            s= child.replace(s);
        return compose(s);
        }

    }

所以,到目前为止,这是正常的,但现在我正在尝试编写一个方便的方法 几个ComposableReplacers并自动堆叠它们:

public static <I, O> ComposableReplacer<I, O> compose(ComposableReplacer<?, ?>... rs)
    {
    for (int i=0; i<rs.length-1; i++)
        rs[i].child= rs[i+1];
    return rs[0];
    }

这会失败,因为每个ComposableReplacer的内部类型必须是其子类型的外部类型,并且编译器无法从ComposableReplacer<?, ?>数组中推断出它。

是否可以在java中执行此操作(并且仍具有类型安全性)?

修改 要清楚,问题是声明一个方法,它采用ComposableReplacer数组并堆栈/链接它们,类型安全。

3 个答案:

答案 0 :(得分:3)

即使支持通用数组,您的代码也会因逻辑错误而失败。数组由相同类型的元素组成,但您要执行的操作不适用于相同类型的项。如果您尝试使用两个参数而不是varargs来实现您的方法,这一点就变得清晰了:

// won’t work
public static <I, O> ComposableReplacer<I, O> compose(
  ComposableReplacer<I, O> rs1, ComposableReplacer<I, O> rs2) {
  rs1.child=rs2;
  return rs1;
}

此代码仍然无法编译,因为rs1.child需要ComposableReplacer<O,?>而不是ComposableReplacer<I,O>如果你解决这个问题,你的方法就会变成

public static <I, O> ComposableReplacer<I, O> compose(
  ComposableReplacer<I, O> rs1, ComposableReplacer<O,?> rs2) {
  rs1.child=rs2;
  return rs1;
}

现在它可以工作,但这两个参数有不同的类型。如果Java具有类型安全的数组,则必须同时阻止包含ComposableReplacer<I, O>ComposableReplacer<O,?>。 (除非您强制OI相同。)

为了进一步说明,这里有三个参数的方法:

public static <I, O, X> ComposableReplacer<I, O> compose(
  ComposableReplacer<I, O> rs1, ComposableReplacer<O,X> rs2,
  ComposableReplacer<X, ?> rs3) {
  rs1.child=rs2;
  rs2.child=rs3;
  return rs1;
}

在这里您可以看到每个参数都有不同的类型,并且您需要一个额外的类型参数,因此使用“类型安全数组”(读取java.util.List)无法提供。最简单的解决方案是保持两个参数方法,让调用者多次调用它。或者n-arg如果你知道经常需要n args的用例。

答案 1 :(得分:0)

我会做那样的事情来实现目标

可以完成组合:

  • 如果Inner和Outer不同:将子替换者传递给父级 代用品
  • 如果Inner和Outer相等:

    1. 将儿童代孕人转为父母代孕人,
    2. 使用compose static方法将child设置为必需。

abstract class Replacer<Outer, Inner> {

    private Replacer<Inner, ?> child;

    protected abstract Inner decompose(Outer something);

    protected abstract Outer compose(Inner something);

    protected abstract Inner inner_replace(Inner something);

    public Replacer(Replacer<Inner, ?> child) {
        this.child = child;
    }

    public Outer replace(Outer something) {
        Inner s = decompose(something);
        s = inner_replace(s);
        if (child != null)
            s = child.replace(s);
        return compose(s);
    }

    public void setChild(Replacer<Inner, ?> child) {
        this.child = child;
    }

    @SafeVarargs
    public static <T> Replacer<T, T> compose(Replacer<T, T>... replacers) {
        if (replacers.length == 0)
            return new DummyReplacer<>();
        else {
            Replacer<T, T> current = replacers[0];
            for (int i = 1; i < replacers.length; ++i) {
                current.setChild(replacers[i]);
                current = replacers[i];
            }
            return replacers[0];
        }
    }
}

class DummyReplacer<Outer> extends Replacer<Outer, Outer> {

    public DummyReplacer(Replacer<Outer, ?> child) {
        super(child);
    }

    public DummyReplacer() {
        super(null);
    }

    @Override
    protected Outer decompose(Outer something) {
        return something;
    }

    @Override
    protected Outer compose(Outer something) {
        return something;
    }

    @Override
    protected Outer inner_replace(Outer something) {
        return something;
    }

}

class Multiply extends Replacer<Integer, Integer> {

    private int factor;

    public Multiply(int factor, Replacer<Integer, ?> child) {
        super(child);
        this.factor = factor;
    }

    public Multiply(int factor) {
        super(null);
        this.factor = factor;
    }

    @Override
    protected Integer decompose(Integer something) {
        return something;
    }

    @Override
    protected Integer compose(Integer something) {
        return something;
    }

    @Override
    protected Integer inner_replace(Integer something) {
        return something * factor;
    }

}

class Negate extends Replacer<String, Integer> {
    public Negate(Replacer<Integer, ?> child) {
        super(child);
    }

    public Negate() {
        super(null);
    }

    @Override
    protected Integer inner_replace(Integer something) {
        return -something;
    }

    @Override
    protected Integer decompose(String something) {
        return Integer.parseInt(something);
    }

    @Override
    protected String compose(Integer something) {
        return something.toString();
    }
}

class SharpToTildeExtract extends Replacer<String, String> {
    public SharpToTildeExtract(Replacer<String, ?> child) {
        super(child);
    }

    public SharpToTildeExtract() {
        super(null);
    }

    @Override
    protected String decompose(String something) {
        return something.substring(1, something.length() - 1);
    }

    @Override
    protected String compose(String something) {
        return "~" + something + "~";
    }

    @Override
    protected String inner_replace(String something) {
        return something;
    }
}

class UpperCaseReplacer extends Replacer<String, String> {

    public UpperCaseReplacer(Replacer<String, ?> child) {
        super(child);
    }

    public UpperCaseReplacer() {
        super(null);
    }

    @Override
    protected String decompose(String something) {
        return something;
    }

    @Override
    protected String compose(String something) {
        return something;
    }

    @Override
    protected String inner_replace(String something) {
        return something.toUpperCase();
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println(new SharpToTildeExtract().replace("#abc#"));
        System.out.println(new SharpToTildeExtract(new UpperCaseReplacer()).replace("#abc#"));
        System.out.println(Replacer.compose(new SharpToTildeExtract(), new UpperCaseReplacer()).replace("#abc#"));

        System.out.println(new SharpToTildeExtract(new Negate(new Multiply(2))).replace("#5#"));
    }
}

答案 2 :(得分:0)

ComposableReplacer就是问题所在。

试试这个:

public abstract class Replacer< Outer, Inner > {
    private static class DelegatingReplacer< Outer, Inner > {
        private final Replacer< Outer, Inner > rplDelegate;
        public DelegatingReplacer( Replacer< Outer, Inner > rplDelegate ) { this.rplDelegate = rplDelegate; }

        @Override protected Inner decompose( Outer something ) { return rplDelegate.decompose( something ); }
        @Override protected Outer compose( Inner something ) { return rplDelegate.compose( something ); }
        @Override protected Inner inner_replace( Inner something ) { return rplDelegate.inner_replace( something ); }
    }
    protected abstract Inner decompose( Outer something );
    protected abstract Outer compose( Inner something );
    protected abstract Inner inner_replace( Inner something );
    public final Outer replace( Outer something ) {
        return compose( inner_replace( decompose( something ) ) );
    }
    public < Innerer > Replacer< Outer, Inner > chain( final Replacer< Inner, Innerer > rplChained ) {
        return new DelegatingReplacer< Outer, Inner >( this ) {
            @Override protected inner_replace( Inner something ) {
                 return rplChained.replace( super.inner_replace( something ) );
            }
        }
    }
}

现在你可以做到

r1.chain( r2.chain( r3 ) ).replace( foo ); // for r1< O, I >, r2< I, J >, r3< J, K > 
r1.chain( r2 ).chain( r3 ).replace( foo ); // for r1< O, I >, r2< I, J >, r3< I, K >

您不能指望Replacer的异构数组以静态类型安全的方式组合。但是,如果所述阵列的构造被认为是静态类型安全的,那么这意味着您可以静态地确定元素的相互类型兼容性,并且可以如上所示构建您的链。

想要基于数组的方法的唯一原因是在运行时确定替换器(以及扩展名的类型)。所以你的问题(如何保证在运行时确定类型的数组的编译时类型安全性)似乎毫无意义。