Java или Scala: создание новых типов во время выполнения - PullRequest
3 голосов
/ 17 октября 2011

Как определить новые типы во время выполнения?У меня есть метод фабрики, который должен создать новый экземпляр this.type с интерфейсом маркера .Интерфейс маркера не был смешан во время компиляции.Мне нужно найти способ сделать это во время выполнения .

Я использую Scala, но я думаю, что ответ будет достаточно общим, чтобы охватить как Java, так и Scala.

trait Fruit {
    def eat: this.type with Eaten = {
        getClass.getConstructors()(0).newInstance(Array()).asInstanceOf[this.type]; 
        // somehow this needs to return a new instance of this.type with the Eaten trait
        // note that "Apple with Eaten" is not a type that exists at compile-time
    }
}

trait Eaten // marker interface

class Apple extends Fruit

val apple1 = new Apple
val apple2 = a.eat // should return a new Apple with Eaten instance

def eater(eaten: Eaten) = ... // this function only accepts Eaten fruit

eater(apple1) // wont compile!
eater(apple2) // will compile!

Ответы [ 5 ]

5 голосов
/ 17 октября 2011

Это невозможно. Конечно, есть способы создания новых классов во время выполнения: просто используйте любой байт-код манипуляция библиотека . Но this.type является не"классом this", а единственным типом this (и нет способа выразить "класс this" в сигнатуре типа Scala )! Итак

def eat: this.type with Eaten = {
    // may do something here, but in the end you have to return
    this
}

И, конечно, если Apple не расширяет Eaten, он не скомпилируется, что бы вы ни делали внутри метода. Обычный обходной путь - что-то вроде

class Fruit[F : Manifest <: Fruit[F]] {
  def eat: F with Eaten = {
    val clazz = manifest[F].erasure
    val result = // do your bytecode manipulations here
    result.asInstanceOf[F with Eaten]
  }
}

но это не сработает, если у вас более одного интерфейса маркера:

val apple = new Apple // Apple
val washed = apple.wash // Apple with Washed
val eaten = washed.eat // Apple with Eaten, but no longer Washed!
2 голосов
/ 17 октября 2011

Я не совсем уверен, какую проблему вы пытаетесь решить, но, возможно, вместо реализации черты вы можете использовать что-то вроде конструктора типов, поэтому Eaten становится чем-то вроде

class Eaten[T]

и Apple.eat возвращает

Eaten[Apple]
1 голос
/ 17 октября 2011

Ну, в Scala есть такая вещь, как неявные преобразования , которую вы можете использовать. Но в Java нет аналога.

Ваш код будет выглядеть примерно так:

implicit def fruit2eaten(fruit: Fruit) = // some way of creating an Eaten from a fruit here.
1 голос
/ 17 октября 2011

JDK6 позволит вам скомпилировать фактический код Java.См. http://www.java2s.com/Code/Java/JDK-6/CompilingfromMemory.htm

В качестве альтернативы (особенно если вы хотите создать класс для реализации интерфейса), вы должны проверить: java.lang.reflect.Proxy, что позволит вам сделать что-то вроде этого:

 InvocationHandler handler = new MyInvocationHandler(...);
 Class proxyClass = Proxy.getProxyClass(
     Foo.class.getClassLoader(), new Class[] { Foo.class });
 Foo f = (Foo) proxyClass.
     getConstructor(new Class[] { InvocationHandler.class }).
     newInstance(new Object[] { handler });

Обратите внимание, что JMock и т.п. также делают это очень просто.

0 голосов
/ 17 октября 2011

Насколько я знаю (что немного), scala не является динамическим языком, это скорее функциональный язык.Теперь в groovy - это динамический язык, вы можете определить класс в строке или в текстовом файле и EVAL его во время выполнения, но я не верю, что это возможно в Scala.

Редактировать: некоторые динамические функции поступают в Scala

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...