2013-12-25 3 views
9

Я хочу вызвать некоторый Java-код из кода Scala. Я хотел бы использовать применить конструкцию Scala, так что я могу назвать это так:Как реализовать метод применения Scala в Java

val led = OutPin(0) 

вместо:

val led = new OutPin(0) 

я наивно реализован дополнительный применять метод в моем Java кода, как это:

public class OutPin { 

    public OutPin(int pinNumber) { 
    } 

    public OutPin apply(int pinNumber) { 
     return new OutPin(pinNumber); 
    } 
} 

Это не делает мой Scala (код первой строки выше) компилировать, и вместо этого дает мне ошибку:

Объекта OutPin не является значение

Что такое правильный способ реализации Scala применит метод в Java?

+0

Попробуйте сделать 'apply' static:' public static OutPin apply (int pinNumber) ' – senia

+1

Есть ли причина, по которой вы не просто реализуете функцию apply в Scala? Я думаю, как минимум, вам нужно будет применить static и сделать статический импорт функции apply. – Felix

+0

На самом деле, я раньше делал это как статический метод, это не имеет значения. –

ответ

13

Ваша проблема заключается не в методе приложения, а в попытке реализовать объект Singleton Scala в Java.

Я думаю (но не уверен), что это очень сложно, возможно даже невозможно, по дизайну.

Рассмотрим очень, очень простой случай:

object Obj; 

Это составляет до двух bytcode файлов виртуальной машины Java, Obj$.class и Obj.class. Теоретически, просто просто проверить байт-код этих двух классов и повторить то же самое на Java. Базовая структура Scala одноэлементных объектов очень, очень проста:

  1. Для одноточечного объекта Obj, A Obj.class и Obj$.class должны быть сгенерированы
  2. Obj$ класса должен иметь public final static поля типа Obj$ под названием MODULE$, который будет инициализирован инициализацией класса, относящейся к объекту singleton. В Scala вызовы Obj.foo() отображаются в Obj$.MODULE$.foo() [... если у Obj был метод с именем foo(), то есть!]
  3. Компилятор Java ничего не знает об этих сгенерированных классах Scala, поэтому для Java interop Obj класс содержит статические функции, которые просто пересылаются на вызов метода с тем же именем и сигнатурой на Obj$.MODULE$.

Звучит сложно, но это действительно не так много. Тривиально писать пару классов Java, которые идут так далеко. Но компилятор Scala (2.10.3) по-прежнему не признает пару как составляющую одноэлементную систему Scala. Погрузившись в байт-код генерируемого синтаксисом Scala-компилятора, вы увидите, что есть детали, которые трудно выразить в юридической Java. [Подсказка: javap -c -p -s -v <fully-qualified-class-name>]

Например, конечное статическое поле MODULE$ инициализируется косвенно статическим инициализатором. Статический инициализатор просто создает объект Obj$ без прямого его назначения. Назначение происходит внутри частного конструктора.Это незаконно в Java: пустые статические финалы обязательно должны инициализироваться в статическом инициализаторе и не могут быть назначены в коде (например, частный конструктор), который потенциально может быть вызван извне инициализатора и несколько раз. Компилятор Scala генерирует байт-код, который уважает пустую конечную семантику (поскольку частный конструктор вызывается только один раз), но превосходит способность компилятора Java проверять эти семантики. Таким образом, этот код будет отклонен, если он выражен в Java.

Кроме того, Obj класс (версия без знака доллара терминал) включает в себя аннотацию типа ScalaSig, который выглядит довольно сложным, и было бы трудно воспроизвести вручную (в Java или Scala), по крайней мере, для тех, мы точно не знаем, как работает эта аннотация.

Я не знаю точно, что ищет компилятор Scala, прежде чем принять решение рассматривать пару классов как «значение», то есть действительный объект Single Scala Scala, но дизайнеры Scala решили не облегчать работу, несмотря на простота базовой схемы. Вероятно, они хотят сохранить способность реорганизовывать, как объекты scala singleton переводят на байт-код. Предоставление Java-программистам синтезирования одноэлементных объектов scala фактически превратит текущую схему в постоянную часть публичного API Scala.

Обратите внимание, что запись обычного, не-одиночного класса, экземпляры которого имеют метод apply (...) и поэтому могут быть названы подобными функциями, легко из Java и отлично работает. Вот Java кошка:

public class Cat { 
    public String apply(int i) { 
     return "Meow: " + i; 
    } 
} 

Вот это использование в Scala засахаренные применяются:

Welcome to Scala version 2.10.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_45). 
Type in expressions to have them evaluated. 
Type :help for more information. 

scala> val Morris = new Cat; 
Morris: Cat = [email protected] 

scala> Morris(8) 
res0: String = Meow: 8 
1

Если вы хотите метод применения, который будет доступен в Scala вы должны реализовать его на Скале в сторону, используя object к обернуть класс Java с тем же именем класса, который его экземпляр

object OutPin { 

    def apply(pinNumber :Int) = new OutPin(pinNumber) 

} 
0

проблема заключается в том, что компилятор Scala говорит, что вы должны определить объекты два компаньона в том же е ile, в противном случае вы получите ошибку:

Companions 'class OutPin' and 'object OutPin' must be defined in same file 

Вот еще один подход, который может сработать для вас. Если вы определите свой Scala OutPin в отдельном пакете, он будет работать. Например:

Java класс:

package base; 

public class OutPin { 
    private final int i; 

    public OutPin(int i) { 
     this.i = i; 
    } 

    public int getI() { 
     return this.i; 
    } 
} 

Scala класс:

package base.scala 

object OutPin { 
    def apply(i: Int): base.OutPin = new base.OutPin(i) 
} 

Sample Scala клиент:

import base.scala.OutPin 

object Client extends App { 
    val op = OutPin(1) 

    println(op.getI) 
} 

Запуск отпечатков клиента 1

Для получить доступ к sugared apply вам нужно будет импортировать base.scala вместо base. Если действительно важно получить синтаксис apply, это может стоить того.

Смежные вопросы