【问题标题】:Casting Predicate<> to a child type将 Predicate<> 转换为子类型
【发布时间】:2020-11-16 19:33:07
【问题描述】:

我有一个Predicate 的自定义实现,我想在某些操作中使用它。 但是,我有一个很难使用多态性的类型。

经过一番调查,我在下面编写了最少的代码来重现问题(这是对问题的更好解释,比我能描述的更好)。

class Custom implements Predicate<Integer> {
    int y;
    
    public Custom(int y) {
        this.y = y;
    }
    
    @Override
    public boolean test(Integer i) {
        return y+i>0;
    }
}


public class Main{
    
    public static void main(String[] args) {
            Custom c1 = new Custom(5);
            Custom c2 = new Custom(8);
            Custom c = (Custom) c1.and(c2); // faulty line - unable to cast
    }
}

我不确定转换失败的原因以及如何使其工作。

【问题讨论】:

  • 默认and 方法的结果返回Predicate 的实现,它不知道您的Custom。要使其工作,请覆盖 Custom 中的 and 方法。但通常你不应该做这样的演员,而只是更喜欢Predicate&lt;T&gt;
  • @fluffy 如何覆盖and ?我做了一些尝试,但失败了。另外,我创建了Custom 类,因为其中有y 属性(我需要保持一些状态)。有没有其他方法可以解决它?
  • 我的意思是,您真的需要转换为Custom 而不是将其称为Predicate&lt;Integer&gt;吗?
  • 多态和“子类型”通常适用于extends(派生类)的使用,而不是implements(实现接口)。我并不是说你应该扩展 Predicate,只是说这里发生了一些事情。
  • @fluffy test 根据Custom 对象的状态进行复杂的正则表达式匹配。我应该可以做到 custom.sety(newY) ,这会影响测试结果。因此,我实现了一个自定义谓词,而不是像函数式编程那样仅使用它。

标签: java casting polymorphism functional-interface


【解决方案1】:

如果您想保留Custom 对象的状态并实现Predicate 接口,我建议重载andornegate 方法。当您将两个Custom 对象与andor 组合时,或者当您调用negate 时,您将得到一个Custom 对象作为返回值。当您将Custom 对象与Predicate&lt;Integer 的任何其他实现结合使用时,这些方法仍将返回Predicate&lt;Integer

class Custom implements Predicate<Integer> {

    class And extends Custom {

        Custom a;
        Custom b;

        public And(Custom a, Custom b) {
            super((i) -> a.test(i) && b.test(i));
            this.a = a;
            this.b = b;
        }
    }

    class Or extends Custom {

        Custom a;
        Custom b;

        public Or(Custom a, Custom b) {
            super((i) -> a.test(i) || b.test(i));
            this.a = a;
            this.b = b;
        }
    }

    class Not extends Custom {

        Custom custom;

        public Not(Custom custom) {
            super((i) -> !custom.test(i));
            this.custom = custom;
        }
    }

    private final Predicate<Integer> predicate;

    public Custom(int y) {
        this((i) -> y + i > 0);
    }

    private Custom(Predicate<Integer> predicate) {
        this.predicate = predicate;
    }

    @Override
    public boolean test(Integer i) {
        return predicate.test(i);
    }

    public Custom.And and(Custom other) {
        return new Custom.And(this, other);
    }

    public Custom.Or or(Custom other) {
        return new Custom.Or(this, other);
    }

    public Custom.Not negate() {
        return new Custom.Not(this);
    }

}

【讨论】:

    【解决方案2】:

    我认为创建这种类型的谓词没有充分的理由,因为它会使您的谓词复杂化。但是,我想到了至少 3 种不同的方式来“改变”谓词状态。

    v0 - 只需使用java.util.function.Predicate&lt;T&gt;

    final Predicate<String> p1 = "foo"::equals;
    final Predicate<String> unit1 = p1.or("bar"::equals);
    Assertions.assertTrue(unit1.test("foo"));
    Assertions.assertTrue(unit1.test("bar"));
    Assertions.assertFalse(unit1.test("baz"));
    final Predicate<String> unit2 = p1.or("baz"::equals);
    Assertions.assertTrue(unit2.test("foo"));
    Assertions.assertTrue(unit2.test("baz"));
    

    这段代码没有任何问题,我仍然会使用它不实现任何自定义类。

    v1 - 在自定义谓词实现中“强制转换”

    这仍然需要覆盖 Predicate&lt;T&gt; 接口中的所有默认方法,以免在未来的 Java 版本中中断。

    public abstract class V1MutablePredicate<T, P extends V1MutablePredicate<T, P>>
            implements Predicate<T> {
    
        @Nullable
        private final Predicate<T> predicate;
    
        protected V1MutablePredicate(@Nullable final Predicate<T> predicate) {
            this.predicate = predicate;
        }
    
        protected abstract boolean doTest(T t);
    
        @Nonnull
        protected abstract P wrap(@Nonnull Predicate<T> predicate);
    
        @Override
        public final boolean test(final T t) {
            return predicate == null ? doTest(t) : predicate.test(t);
        }
    
        @Nonnull
        @Override
        public final P and(@Nonnull final Predicate<? super T> other) {
            return wrap(Predicate.super.and(other));
        }
    
        @Nonnull
        @Override
        public final P negate() {
            return wrap(Predicate.super.negate());
        }
    
        @Nonnull
        @Override
        public final P or(@Nonnull final Predicate<? super T> other) {
            return wrap(Predicate.super.or(other));
        }
    
    }
    
    private static final class Custom
            extends V1MutablePredicate<String, Custom> {
    
        private String s;
    
        Custom(final String s) {
            this(null, s);
        }
    
        private Custom(@Nullable final Predicate<String> predicate, final String s) {
            super(predicate);
            this.s = s;
        }
    
        @Override
        protected boolean doTest(final String t) {
            return t.equals(s);
        }
    
        @Nonnull
        @Override
        protected Custom wrap(@Nonnull final Predicate<String> predicate) {
            return new Custom(predicate, s);
        }
    
    }
    
    @Test
    public void test() {
        final Custom p1 = new Custom("foo");
        final Custom p2 = new Custom("bar");
        final Custom unit = p1.or(p2);
        Assertions.assertTrue(unit.test("foo"));
        Assertions.assertTrue(unit.test("bar"));
        Assertions.assertFalse(unit.test("baz"));
        p2.s = "baz";
        Assertions.assertTrue(unit.test("foo"));
        Assertions.assertTrue(unit.test("baz"));
    }
    

    这似乎最接近您想要完成的目标。

    v2 - 从外部注入谓词状态

    public final class V2MutablePredicate<T, S>
            implements Predicate<T> {
    
        private final Supplier<? extends S> stateSupplier;
        private final BiPredicate<? super S, ? super T> predicate;
    
        public V2MutablePredicate(final Supplier<? extends S> stateSupplier, final BiPredicate<? super S, ? super T> predicate) {
            this.stateSupplier = stateSupplier;
            this.predicate = predicate;
        }
    
        @Override
        public boolean test(final T t) {
            return predicate.test(stateSupplier.get(), t);
        }
    
    }
    
    final AtomicReference<String> r1 = new AtomicReference<>("foo");
    final V2MutablePredicate<String, String> p1 = new V2MutablePredicate<>(r1::get, String::equals);
    final AtomicReference<String> r2 = new AtomicReference<>("bar");
    final V2MutablePredicate<String, String> p2 = new V2MutablePredicate<>(r2::get, String::equals);
    final Predicate<String> unit = p1.or(p2);
    Assertions.assertTrue(unit.test("foo"));
    Assertions.assertTrue(unit.test("bar"));
    Assertions.assertFalse(unit.test("baz"));
    r2.set("baz");
    Assertions.assertTrue(unit.test("foo"));
    Assertions.assertTrue(unit.test("baz"));
    

    此实现需要从管理多个要处理的对象的外部更改状态,并且还需要“状态”类,但它不需要覆盖默认方法,并且还需要供应商在每个对象中提供值test方法调用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多