12

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

一个简单的例子是一个类,它接受字符串“--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并堆栈/链接它们,具有类型安全性。

4

3 回答 3

3

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

// 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,?>而不是 a ,您的方法将变为ComposableReplacer<I,O>

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

现在它可以工作了,但是这两个参数有不同的类型。如果 Java 有类型安全的数组,他们必须防止同时包含 aComposableReplacer<I, O>和 a 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;
}

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

于 2013-11-08T14:36:42.397 回答
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 >

您不能期望Replacers 的异构数组以静态类型安全的方式组成。但是,如果应该保证所述数组的构造是静态类型安全的,那将意味着您可以静态确定元素的相互类型兼容性,并且可以构造如上所示的链。

需要基于数组的方法的唯一原因是替换器(以及它们的类型)是否在运行时确定。所以你的问题(如何保证在运行时确定类型的数组的编译时类型安全)似乎没有意义。

于 2013-11-08T20:11:28.447 回答
0

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

可以进行组合:

  • 如果 Inner 和 Outer 不同:将子替换器传递给父替换器
  • 如果 Inner 和 Outer 相等:

    1. 将子替换器发送给父替换器,
    2. 使用根据需要设置 child 的 compose 静态方法。

.

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#"));
    }
}
于 2013-11-08T18:28:46.730 回答