Устранение неоднозначности интерфейса Java в параметрах метода

У меня есть дерево логических операций из логического выражения, которое мне нужно запустить в Java. Дерево будет примерно таким: И мне нужно было бы запускать его на Java итеративно, чтобы он возвращал мне логическое значение.

boolean b = AND(isNumberEven("4"), AND(isNumberOdd("7"), isNumberEven("6"))

Проблема в том, что мне нужно написать интерфейсы, так как в моей компании мы могли бы создать множество классов, таких как And, Or, isSomething, hasInArray (это может быть любое свойство, необходимое другим разработчикам). Обычно у нас есть два типа узлов: логические, которые имеют две ветви, и узлы проверки свойств, которые являются листьями дерева.

То, что я пытался сделать ниже, я думаю, что это действительно неправильно в Java, пожалуйста, помогите мне сделать это правильно.

Моя идея состояла в том, чтобы создать два интерфейса, один для логических операторов, а другой для проверки свойств. Средство проверки свойств должно оценить свое свойство на входе, в то время как логический оператор должен оценить две ветви, а затем что-то вычислить с двумя результатами (например, И или ИЛИ, нас пока не волнует оператор НЕ). . Я думал сделать эти интерфейсы

class PropertyCheckingInput {
   String s; // dummy property, this object is the thing we want to verify the property on (example, if this string is an even number)

   PropertyCheckingInput(...){...} // constructor for all params
}

interface PropertyChecking {
   boolean fire(PropertyCheckingInput i);
}

class LogicOperatorInput<
   T extends LogicOperator or PropertyChecking, // it can be one or the other
   Ti extends LogicOperatorInput or PropertyCheckingInput
> {
    T left;
    Ti leftInput;
    T right;
    Ti rightInput;

    // probabily the type of leftInput should act accordingly to the type of left, but I don't know how to enforce this in Java with generics

    LogicOperatorInput(...){...} // constructor for all params
}

interface LogicOperator{
   boolean fire(LogicOperatorInput i); 
}

В этом случае, если я хочу реализовать что-то вроде И, я мог бы сделать это как

class And implements LogicOperator {
    boolean fire(LogicOperatorInput i) {
        i.left.fire(i.leftInput) && i.right.fire(i.rightInput);
    }

    And() {}

    public static void main(String[] args) {
        // expression: isNumberEven("4") AND isNumberOdd("7") AND isNumberEven("6")
        boolean b = new And().fire(new LogicOperatorInput(
            new isNumberEven(), 
            new PropertyCheckingInput("4"), 
            new And(), 
            new LogicOperatorInput(
                new isNumberOdd(), 
                new PropertyCheckingInput("7"), 
                new isNumberEven(), 
                new PropertyCheckingInput("6"))
        ));

        System.out.println(b);
    }
}

Для каждой ветки я выполняю функцию fire как слева, так и справа с их вводом и забочусь только об их результате. Затем, если я хочу создать логическое выражение, я объединяю различные узлы и входные данные.

Компиляторы, конечно же, говорят мне, что это неправильно, потому что они не могут определить правильный тип функций fire().

Я привык писать на Javascript, который позволяет такие вещи, потому что он не проверяет, что вы пытаетесь сделать. Есть ли способ сделать это в Java (я пытался использовать некоторые дженерики или абстрактные классы, но это не сработало) или, что еще лучше, правильный способ решить эту проблему (где у нас есть двоичное выражение, записанное в виде двоичного дерева, и мы хотите разрешить это, вызвав класс, связанный с каждым узлом)?

ну, ваша основная проблема в том, что вы пытаетесь сказать, что T в LogicOperator является LogicOperator или PropertyChecking. затем в вашем And.fire() вы пытаетесь вызвать left.fire() с одним аргументом. однако, если left является LogicOperator, то метод fire() принимает 4 аргумента, а не 1. Я не могу сказать, что полностью понимаю, что вы пытаетесь сделать, но, может быть, это поможет?

jtahlborn 06.07.2023 23:36

Я тоже не понимаю, что вы пытаетесь сделать, поэтому вам сложно помочь. Вы говорите, что вам нужно дерево выражений, но ваш пример использования не создает и не использует дерево, он просто вызывает некоторые функции и передает логические значения, которые они возвращают, в функцию добавления, которая возвращает логическое значение. Где в нем дерево?

meriton 06.07.2023 23:47

Что такое A, B и C в вашем примере? Я не понимаю, почему логический оператор должен принимать четыре аргумента.

daniu 06.07.2023 23:48

Наличие Ti в качестве параметра для fire() кажется немного неудобным (нарушение инкапсуляции). Похоже, что это будет единый интерфейс (или, может быть, адаптер), который может обернуть настоящие T и Ti. Тогда And и Or могут принимать переменное количество аргументов и делегировать их обертке/адаптеру.

Andrew S 06.07.2023 23:53

Прошу прощения за путаницу, я пытался переписать примеры

simondx 07.07.2023 00:40
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
5
75
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Возможно, вы слишком усложняете это. Если вам нужно дерево выражений, как на изображении, создайте дерево выражений. Вам не нужно различать «операторы» и «ввод».

// this represents a tree "node"
interface BooleanExpression {
    // all nodes should implement this to represent how they are evaluated
    boolean evaluate();
}

// a node can be any of these types
record IsNumberEven(int number) implements BooleanExpression {
    @Override
    public boolean evaluate() {
        return number() % 2 == 0;
    }
}

record IsNumberOdd(int number) implements BooleanExpression {
    @Override
    public boolean evaluate() {
        return number() % 2 == 1;
    }
}

record And(BooleanExpression left, BooleanExpression right) implements BooleanExpression {
    @Override
    public boolean evaluate() {
        return left().evaluate() && right().evaluate();
    }
}

Использование:

boolean result = new And(
        new IsNumberEven(4),
        new And(
                new IsNumberOdd(7),
                new IsNumberEven(6)
        )
).evaluate();

Если вы действительно хотите отделить «входы» узлов IsNumberOdd/IsNumberEven, вы можете это сделать. Просто обобщите BooleanExpression на Expression<T>

interface Expression<T> {
    T evaluate();
}

record Constant<T>(T value) implements Expression<T> {
    @Override
    public T evaluate() {
        return value();
    }
}

record IsNumberEven(Expression<Integer> number) implements Expression<Boolean> {
    @Override
    public Boolean evaluate() {
        return number().evaluate() % 2 == 0;
    }
}

record IsNumberOdd(Expression<Integer> number) implements Expression<Boolean> {
    @Override
    public Boolean evaluate() {
        return number().evaluate() % 2 == 1;
    }
}

record And(Expression<Boolean> left, Expression<Boolean> right) implements Expression<Boolean> {
    @Override
    public Boolean evaluate() {
        return left().evaluate() && right().evaluate();
    }
}

Использование:

boolean result = new And(
        new IsNumberEven(new Constant<>(4)),
        new And(
                new IsNumberOdd(new Constant<>(7)),
                new IsNumberEven(new Constant<>(6))
        )
).evaluate();

Я не думал о добавлении ввода вне метода. Спасибо!

simondx 07.07.2023 13:39

Другие вопросы по теме