Можно ли объединить два типа для создания другого TypeLiteral? - PullRequest
0 голосов
/ 06 ноября 2018

Учитывая сигнатуру этого метода, возможно ли это реализовать? Если да, то как?

TypeLiteral<MyClass<T, R>> combineTypes(Class<T> typeOne, Class<R> typeTwo) {
  // Awesome implementation here
}

Фон

У меня есть интерфейс, ApiHandler<TReq, TRes>, который я пытаюсь создать на фабрике, с Guice, данного типа TReq. Я также могу при необходимости ввести TRes. К сожалению, TReq и TRes не имеют значимых родительских классов или интерфейсов по независящим от меня причинам (они созданы из Apache Thrift).

Ответы [ 3 ]

0 голосов
/ 06 ноября 2018

TypeLiteral имеет два конструктора; обычный при использовании по назначению TypeLiteral<MyClass<Foo, Bar>>() {} и небезопасный, который не является универсальным и создается непосредственно: new TypeLiteral(type). Глядя на код для TypeLiteral Guice здесь , мы видим, что обычный конструктор использует parameterized.getActualTypeArguments()[0] для этого параметра, где parameterized является ParameterizedType. Чтобы сделать что-то, что соответствует MyClass<R, T>, это будет другой ParameterizedType, но с двумя параметрами, а не одним, и каждый параметр будет простым классом. Теперь вы можете создать реализацию интерфейса ParameterizedType, которая выполняет необходимый контракт, создать TypeLiteral и привести его к правильному возвращаемому значению (у него не будет правильного универсального типа, вам придется выполнить небезопасный актерский состав).

Это выглядит так:

TypeLiteral<MyClass<T, R>> combineTypes(Class<T> typeOne, Class<R> typeTwo) {
    return new TypeLiteral(new ParameterizedType() {
        @RecentlyNonNull
        Type[] getActualTypeArguments() {
            return new Type[] {typeOne, typeTwo};
        }

        @RecentlyNonNull
        Type getRawType() {
            return MyClass.class;
        }

        Type getOwnerType() {
            // this is only needed for nested classes, eg. if this was Foo.Myclass this would return Foo.class.
            return null;
        }

    });
}
0 голосов
/ 07 ноября 2018

Если вы вычисляете его на основе аргументов времени выполнения, он больше не литерал : это просто тип.

Несмотря на то, что вы могли бы использовать эквивалентный каркас Guava для TypeToken и его утилит, в Guice есть встроенный класс утилит Type: com.google.inject.util.Types . Используйте newParameterizedType(Type rawType, Type... typeArguments) для создания требуемого типа, отметив, что ParameterizedType и Class оба реализуют тип.

static ParameterizedType combineTypes(Class<?> typeOne, Class<?> typeTwo) {
  return Types.newParameterizedType(MyClass.class, typeOne, typeTwo);
}

К сожалению, AbstractModule.bind и LinkedBindingBuilder.to не предлагают перегрузки для Type; просто Class, TypeLiteral и Key. К счастью, вы можете генерировать ключ отражательно, используя тип, используя Key.get(Type):

bind(Key.get(combineTypes(Foo.class, Bar.class))).to(MyClassFooBar.class);

Обратите внимание, что ParameterizedType сам по себе не является параметризованным типом. Это побеждает некоторые хитрые средства защиты на основе генериков, которые предлагает bind EDSL от Guice. Для того чтобы вышеперечисленное сработало, вам может потребоваться @SuppressWarnings, вернуть необработанный тип Key или подумать о том, чтобы combineTypes вернул Key<MyClass<T, R>> (что потребовало бы приведения из возвращаемого значения Key.get(Type) Key<?>). Если вам действительно необходимо использовать TypeLiteral, вы можете создать TypeLiteral с помощью Key.getTypeLiteral , но для этого также потребуется приведение с TypeLiteral<?> - и не будет «литералом типа» по любому значимому определению.

0 голосов
/ 06 ноября 2018

Я раньше не использовал Guice или Thrift, надеюсь, это поможет.

Определение MyClass: public class MyClass<T, R> {}

Реализуйте метод, добавив параметр типа:

public static <T, R> TypeLiteral<MyClass<T, R>> combineTypes(Class<T> typeOne, Class<R> typeTwo) {
    return new TypeLiteral<MyClass<T, R>>() {};
}
public static void main(String[] args) {
    TypeLiteral<MyClass<Integer, String>> myClassTypeLiteral = TypeTest.combineTypes(Integer.class, String.class);
}

Или просто:

public static <T, R> TypeLiteral<MyClass<T, R>> combineTypes() {
    return new TypeLiteral<MyClass<T, R>>() {};
}
public static void main(String[] args) {
    TypeLiteral<MyClass<Integer, String>> myClassTypeLiteral = TypeTest.<Integer, String>combineTypes();
}
...