Общий ИЛИ вместо AND <T расширяет число | CharSequence> - PullRequest
26 голосов
/ 30 ноября 2011

Можно ли в общем параметризовать метод, принимающий EITHER ClassA OR InterfaceB?

Не компилируется из-за |Псевдокод

public <T extends Number | CharSequence> void orDoer(T someData){ // ... }

т.е. вместо написания нескольких сигнатур методов, я бы хотел, чтобы этот один метод принимал в качестве аргумента Number или CharSequence

Должен пройти сЧисло ИЛИ CharSequence аргумент

orDoer(new Integer(6));
int somePrimitive = 4;
orDoer(somePrimitive);
orDoer("a string of chars");

Ответы [ 3 ]

18 голосов
/ 30 ноября 2011

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

public class OrDoerElement {
    private final Number numberValue;
    private final CharSequence charSequenceValue;

    private OrDoerElement(Number number, CharSequence charSequence) {
        this.numberValue = number;
        this.charSequenceValue = charSequence;
    }

    public static OrDoerElement fromCharSequence(CharSequence value) {
        return new OrDoerElement(null, value);
    }

    public static OrDoerElement fromNumber(Number value) {
        return new OrDoerElement(value, null);
    }
}

И ваш orDoer метод становится:

public void orDoer(OrDoerElement someData) { .... }

Затем вы можете создать один из них и использовать в своем методе, используя:

orDoer(OrDoerElement.fromCharSequence("a string of chars"));
orDoer(OrDoerElement.fromNumber(new Integer(6)));

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

1 голос
/ 30 ноября 2011

Является ли использование анонимного абстрактного класса вариантом для вас? Когда мне нужны типовые безопасные параметры или возвращаемые типы, я использую некоторый вариант кода ниже. При этом я согласен с другими комментариями здесь, и мне любопытно, какую выгоду вы действительно получаете, когда вы применяете безопасность типов для группы объектов, которые не имеют ничего общего.

public abstract class Doer<T> {

  public void do(T obj) {
    // do some stuff. 
  }

}

// calling method

new Doer<Number>(){}.do(new Integer(5));
0 голосов
/ 09 августа 2018

Для исходного вопроса:

public void orDoer(Object someData){
    assert someData instanceof Number || someData instanceof CharSequence;

    // ...
}

В более конкретном случае оператор assert должен просто использовать интроспекцию, чтобы выяснить, имеет ли объект нужную вам специфику, то есть проверить конструктор из String, попробуйте создать новый экземпляр объекта из результата toString() входящего объекта и сравните оба на равенство:

public void orDoer(Object someData) {
    assert isUniconstructable(someData);
}

public static boolean isUniconstructable(Object object) {
    try {
        return object.equals(object.getClass().getConstructor(String.class)
            .newInstance(object.toString()));
    } catch (InstantiationException | IllegalAccessException | InvocationTargetException
            | NoSuchMethodException| RuntimeException e) {
        return false;
    }
}

(Из-за исключений, которые могут быть выброшены, нам нужно обернутьтест assert в свою собственную функцию.)

Имейте в виду, что самоанализ может сломаться из-за сжатия кода ProGuard в Android, которое переписывает имена классов, а вместо YourClass просто класс, т.е.Q хранится в базе данных, и когда вы хотите восстановить его с помощью более поздней версии вашего приложения, которая имеет больше классов, класс Q будет чем-то другим.Посетите веб-сайт ProGuard для получения дополнительной информации об этом;Я просто хотел сообщить, что вы должны знать об этом при использовании самоанализа на Android.

...