Я пытаюсь написать общую служебную функцию scala для работы с классами Java, сгенерированными Apache Thrift. Все сгенерированные Thrift Java-классы расширяют интерфейс TBase следующей подписью:

public interface TBase<T extends TBase<?,?>, F extends TFieldIdEnum>

Это, безусловно, всегда было проблематично при создании общих функций служебных функций, и в мире Java я обычно решал эту проблему, просто используя @SuppressWarnings("rawtypes"), зная, что я справлялся с этим правильно, и эффективно игнорируя компилятор, говорящий мне, что он может ' проверить соответствие типов.

В мире scala я, кажется, не могу позволить себе роскошь или игнорирование этих типов, и я столкнулся с ситуацией, когда я не могу понять, как удовлетворить требования универсального типа.

Итак, я создал свою собственную функцию, которая преобразует данный объект, не относящийся к Thrift, в экземпляр запрошенного класса Thrift с сигнатурой, похожей на:

def myFunc[T <: TBase[T, E], E<: TFieldIdEnum](obj: Any, clazz: Class[T]): T = {

Поэтому моя проблема заключается в том, что для вызова этой функции мне нужно сделать что-то вроде:

myFunc[MyThriftClass,MyThriftClass._Fields]( obj, classOf[MyThriftClass] )

Без явно заданного параметра второго типа компиляция завершается неудачно с:

error: inferred type arguments [MyThriftClass,Nothing] do not conform to method myFunc's type parameter bounds [T <: org.apache.thrift.TBase[T,E],E <: org.apache.thrift.TFieldIdEnum]
    myFunc( null, classOf[MyThriftClass])
    ^
error: type mismatch;
found   : Class[MyThriftClass](classOf[MyThriftClass])
required: Class[T]
   myFunc( null, classOf[MyThriftClass])

Когда я передаю оба параметра типа, все работает, хотя я предпочел бы только передать класс.

Но то, что я не могу понять, как заставить это работать, если я не знаю типы во время компиляции.

Например, экономичные метаданные определяются с помощью FieldValueMetaData объектов, а при наличии подструктуры предоставляется объект метаданных StructMetaData, который содержит поле

public final Class<? extends TBase> structClass;

Учитывая, что в этом классе отсутствуют параметры типа, я не знаю, как вызывать с ним мой метод, удовлетворяя ограничениям универсального типа. В случае рекурсии мне удалось обойти это, приведя класс substruct к экземпляру Class[T], что неверно, потому что это родительский тип, а не подтип, но из-за стирания типа это делает компилятор счастливым и работает во время выполнения. Но если у меня еще нет универсального типа для приведения, я не знаю, как справиться с этим, за исключением, может быть, действительно хакерского подхода, когда я приводил его к какой-то фальшивой оболочке класса TBase, просто чтобы сделать компилятор счастливым.

Есть ли правильный способ настроить это, чтобы компилятор был доволен реальными типами? В качестве альтернативы есть способ обойти это так же, как в чистой Java с @SuppressWarnings("rawtypes"). В противном случае, если нет, то, возможно, мне просто придется перенести эту логику на Java и вызвать ее из Scala, чтобы сделать все счастливым. Прежде чем сделать это, я бы хотел узнать, чего мне не хватает, или узнать, почему то, что я хочу, невозможно.

Редактировать:

На самом деле я ищу какой-нибудь способ в scala вызвать мой существующий метод myFunc с Tbase[_,_]. В Java я бы сделал это с подавлением необработанных типов, но в scala я хотел бы знать либо способ удовлетворить типы и разрешить вызов моего метода, либо способ подавить их и разрешить вызов моего метода.

Изменить 2:

Выяснил, как делать то, что хочу, но все еще ищу более чистые решения, если они существуют. В итоге я получил следующее:

def myFunc[T <: TBase[_,_],T2 <: TBase[T2, E2], E2<: TFieldIdEnum](obj: Any, clazz: Class[T]): T = {
  _myFunc[T2,E2]( obj, clazz.asInstanceOf[Class[T2]] ).asInstanceOf[T]
}

def _myFunc[T <: TBase[T, E], E<: TFieldIdEnum](obj: Any, clazz: Class[T]): T = {

Честно говоря, я не знаю, почему это работает, потому что я не указал, какие типы T2 или E2 есть, но, похоже, это заставляет его компилироваться и запускаться.

7
user1084563 21 Дек 2019 в 03:29
Я полагаю, что def myFunc[T <: TBase[T, _ <: TFieldIdEnum]](obj: Any, clazz: Class[T]): T вам не подходит? Но трудно точно знать, почему, или предположить, что сработает, потому что ваша настоящая проблема, похоже, скрывается где-то в коде, который вы не показываете.
 – 
Jasper-M
15 Янв 2020 в 16:03
Это не работает, потому что мне действительно нужно использовать тип E, поэтому он не может быть просто подстановочным знаком. У экономичных классов есть методы setFieldValue и getFieldValue, которые принимают объект типа E в качестве аргумента. К сожалению, для динамического получения TFieldIdEnum объекта мне нужно использовать отражение, поскольку это не то, что отображается с помощью внешнего обобщенно типизированного метода. Поскольку я использую отражение, я фактически получаю объект типа TFieldIdEnum вместо E, и поэтому мне нужен тип E для целей приведения, иначе он не позволит мне вызвать эти методы .
 – 
user1084563
15 Янв 2020 в 22:18

4 ответа

Вот как я представляю себе иерархию файлов:

Это утверждение неверно.

Теперь это сработает, если я вставлю код в python setup.py install, но я просто хочу импортировать отдельные истории в главный файл, и я не могу понять, как это сделать. Я безуспешно пытался подписаться:

Однако это не работает и просто приводит к ошибкам синтаксического анализа forSome. Также кажется неправильным импортировать for num in i:, когда я должен импортировать здесь историю.

def myFunc[T <: TBase[U, E] forSome {type U; type E}, E<: TFieldIdEnum](obj: Any, clazz: Class[T])

При попытке изменить размер asp: Chart с помощью сценария Java как document.getElementById ("StatusGraph1"). Style.width = "500px" в результате размер графика изменяется, но изображение src, которое он создает внутри, не изменяется, и, наконец, пиксели ломаются.

def myFunc[T <: TBase[_, _], E<: TFieldIdEnum](obj: Any, clazz: Class[T])

При желании вы можете избавиться от параметра clazz, вызвав неявный тег класса:

def myFunc[T <: TBase[_, _]: ClassTag, E<: TFieldIdEnum](obj: Any) = {
  val c = implicitly[ClassTag[T]].runtimeClass

Я работаю над набором отношений продукт / категория в приложении Django.

В этом фрагменте кода:

def myFunc[T <: TBase[T, E], E<: TFieldIdEnum](obj: Any, clazz: Class[T]): T = ???

Кажется, что вы пытаетесь выполнить своего рода «каррирование параметра типа», а затем введите вывод параметра. Причуда в том, что scala по умолчанию не имеет каррирования параметров типа таким образом, что некоторые параметры типа могут быть опущены и выведены с помощью scalac. В scala вы должны явно установить параметры типа, которые вы хотите установить, и параметры типа, которые должны быть выведены из использования. Это делается с помощью вспомогательного класса и сопутствующего ему объекта. Имея

  def foo_1[G[_], A](fb: G[A]):G[A] = ???

Который называется:

  val v: List[Int] = ???
  foo_1[List, Int](v)

Потому что, как вы знаете, в некоторых ситуациях не так легко вывести высшие виды. Затем мы вводим специальный пустой case class foo[G[_]](), который используется для переноса этого типа. Мы могли бы создать его экземпляр с помощью метода apply, чтобы иметь хороший синтаксис.


  object foo{
    def apply[G[_]]: foo[G] = new foo[G]()
  }

Затем мы могли бы добавить еще один метод apply, который будет вводить второй параметр типа B

  case class foo[G[_]](){
    def apply[A](fa: G[A]): G[A] = foo_1[G,A](fa)
  }

Тогда у нас мог бы быть сжатый, лишенный шаблонов синтаксис:

  val v: List[Int] = ???

  val v1 = foo_1[List, Int](v)
  val v2 = foo[List](v)
} 

Обратите внимание, что foo[List](v) и foo.apply[List].apply[Int](v) одинаковы из-за синтаксического сахара, связанного с apply.

Место, где этот метод не работает, - это место, где параметр типа B не может быть выведен компилятором. Надеюсь, в вашем случае это не так.

И последнее, что не менее важно, так как вы не просили об этом, но на самом деле вы можете избежать явной передачи clazz: Class[T] в качестве параметра функции, но вместо этого вы можете превратить его в неявный параметр:

def myFunc[T <: TBase[T, E], E<: TFieldIdEnum](obj: Any)(implicit ct: ClassTag[T]): T = ???

И получить доступ к ней как к обычной переменной из вашей функции. Чтобы завершить все, вы должны передать этот тег класса через вспомогательный класс. Кроме того, чтобы использовать этот трюк, вы должны стереть зависимость T от типа E, чтобы разрешить его использование через forSome.

  trait F[A, B]
  trait U

  object foo {
    def apply[T <: F[T, E] forSome {type E}]: foo[T] = new foo[T]
  }

И, чтобы восстановить тип E, вы можете попросить компилятор вывести такой параметр E, который не соответствует требованиям: E <: U и T <: F [T, E] через неявное свидетельство типа <:< , который генерируется компилятором.

  case class foo[T <: F[T, E] forSome {type E}]() {
    def apply[E <: U](a: Any)(implicit e: <:<[T, F[T, E]], ct: ClassTag[T]): T = {
      ???
      //here goes your code.
    }
  }

Невозможно добавить заголовок с использованием класса аутентификатора okhttp3 в android

Вот как следует использовать синтаксис:

  trait E extends U
  trait T extends F[T,E]

  foo[T].apply("abc")
5
Iva Kam 24 Дек 2019 в 18:59
Ниже вы можете узнать, что я сделал до сих пор. Просто нажмите на задачу, чтобы увидеть.
 – 
user1084563
30 Дек 2019 в 21:18

И я вызываю эту функцию из mainactivity.java для инициализации и запроса по определенному URL-адресу. это всегда возвращается

Он генерирует собственный код scala, который намного приятнее и идиоматичнее для работы со scala и танцует вокруг (изменяемых !!!) классов java.

Сгенерированные Скруджем структуры расширяют ThriftStruct, который не параметризован, и решает все ваши проблемы мгновенно.

1
Dima 16 Янв 2020 в 22:37
Я подумал об этом, и это правильное предложение, но основная причина, по которой мы этого не сделали, заключается в том, что мы в основном используем java и уже написали множество утилит для бережливых объектов на java, которые все пришлось бы переписать, если бы мы переключили рыскать
 – 
user1084563
17 Янв 2020 в 01:56
Использование Android Zebra tc70 со сканером штрих-кода DataWedge, но не получение кодов конца строки при сканировании
 – 
Dima
17 Янв 2020 в 02:41
Поэтому я начал с scala, потому что этот конкретный проект основан на scala, но после того, как я столкнулся с проблемами, я понял, что мне может быть лучше просто вернуться к java. однако меня действительно беспокоило то, что я не мог заставить его работать, когда я знал, что должен быть способ, и я опубликовал это больше из любопытства, чтобы узнать, как его преодолеть, чем для чего-либо еще. Мне удалось найти свое хакерское решение, которое я опубликовал, но мне все еще было любопытно, есть ли другое более чистое решение.
 – 
user1084563
17 Янв 2020 в 03:37

FrameToBeAvailableAndSwitchToIt () не помогает переключиться на фрейм

def myFunc[T <: TBase[_,_],T2 <: TBase[T2, E2], E2<: TFieldIdEnum](row: Row, clazz: Class[T]): T = {
  _myFunc[T2,E2]( row, clazz.asInstanceOf[Class[T2]] ).asInstanceOf[T]
}

def _myFunc[T <: TBase[T, E], E<: TFieldIdEnum](row: Row, clazz: Class[T]): T = {

К сожалению, ваше решение не помогло. В моем коде нет проблем, он работает так, как ожидалось, что указывает на правильность кода, единственное, что вызывает периодические сбои тестов. Следовательно, мой вопрос в том, как сделать его стабильным, чтобы он проходил каждый раз.

0
user1084563 15 Янв 2020 в 02:45
error: inferred type arguments [MyThriftClass,Nothing] do not conform to method myFunc's type parameter bounds [T <: org.apache.thrift.TBase[T,E],E <: org.apache.thrift.TFieldIdEnum]
    myFunc( null, classOf[MyThriftClass])
    ^
error: type mismatch;
found   : Class[MyThriftClass](classOf[MyThriftClass])
required: Class[T]
   myFunc( null, classOf[MyThriftClass])

Вы можете найти соответствующее обсуждение в нескольких тегах iframe Selenium webdriver

def myFunc[T <: TBase[_, _]: ClassTag](obj: Any): T
0
thirstycrow 16 Янв 2020 в 06:33
Если оба frameA и frameB являются прямыми дочерними элементами контекста просмотра верхнего уровня , то при нахождении в frameA у вас есть в widget_test.dart defaultContent () (который выбирает либо первый фрейм на странице, либо основной документ, если страница содержит фреймы.) сначала, затем во фрейм frameB следующим образом:
 – 
user1084563
16 Янв 2020 в 23:30