как перегрузить методы суперкласса явно или неявно? - PullRequest
0 голосов
/ 06 октября 2011

Есть ли способ в java или scala, который позволяет автоматически переопределять методы суперкласса?

Отказ от ответственности: я не претендую на опытного программиста, пожалуйста, не убивайтеЯ для любых злодеяний кодирования ниже.

У меня есть оболочка B класса A, которая делает некоторые вещи и делегирует два разных экземпляра A. Проблема в том, что класс A имеет очень большое количество методов.Вот моя оригинальная реализация Java .Это монстр с огромным дублированием кода.Это работает, но, вероятно, содержит много ошибок, некоторые из которых, возможно, трудно отследить, некоторые из них просто будут давать неправильные результаты без примечания.

Вот моя первая попытка повторной реализации с использованием структурной типизации scala .

def get(key: Array[Byte]): Array[Byte] = {
 val f: (Transaction) => Response[Array[Byte]] = _.get(key)
 wrap[Array[Byte]](f, redundancySwitch, currentDB, key).get()
 }

остальное оставлено для общей функции переноса.Это уже приятно!Но мне все еще нужно реализовать каждый метод индивидуально и конкретно.

Моей следующей мыслью было попытаться создать единую (отражающую) обобщенную реализацию для копирования в каждое тело переопределяющего метода, чтобы в определении объекта функции f было что-то, что могло бы заполнить имя текущегоВызов метода.

def get(key: Array[Byte]): Array[Byte] = {
 val f: (Transaction) => Response[Array[Byte]] = _.someReflectionMagic.nameOfCurrentlyCallingMethod(args)

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

def zadd(key: String, score: Int, value: String) = {

// get the current Method
val thisMethod = new Object().getClass.getEnclosingMethod
val paramTypes = thisMethod.getParameterTypes
val returnType = thisMethod.getReturnType;

 //  this doesn't exist. how to reflectively forward parameter values (not parameter Types) into an Array for later reflective method invocation?
val params = thisMethod.getParameters  

// I want to avoid to get it manually:
val params = new Array[Object]
params(0) = key
params(1) = score
params(2) = value

// this doesn't work. how to indicate the returnType here?
def f(t: Transaction): Response[returnType.class] =   
 t.getClass.getMethod(thisMethod.getName, paramTypes).
            invoke(t,params).
            asInstanceOf(Response[String]) 

 wrap[String](f, redundancySwitch, currentDB, key).get()
}

private def getMethodName: String = {
 val ste: Array[StackTraceElement] = Thread.currentThread.getStackTrace
 return ste(2).getMethodName
}

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

Этот подход все еще требует наличия> 200 тел методов в моем классе-обертке.Интересно, есть ли какой-нибудь способ полностью обойтись без этого: класс A, обертывающий класс B, который выглядит снаружи, как будто B имеет все методы A, даже явно не реализуя их.Когда B вызывается методом A x (args: T): U, автоматическое переопределение / автоматическое делегирование для одного обобщенного метода By (x: T => U).y () будет что-то вроде метода wrap () во второй ссылке.Однако многие из вышеперечисленных проблем остаются ... то есть, как захватить аргументы и применить имя метода к структурному типизированному значению ...

имеет ли это смысл?Это не представляется возможным с помощью отражения Java / Java.Есть ли в скале что-нибудь, что могло бы мне помочь?Может быть, какое-нибудь экспериментальное отражение в скале?

Большое спасибо за любые подсказки

Ответы [ 2 ]

3 голосов
/ 06 октября 2011

Одним из способов будет использование java.lang.reflect.Proxy . Это потребовало бы, чтобы у вас был интерфейс для всех ваших методов, но вы можете получить это достаточно легко, используя Refactor-> Extract Interface в Eclipse.

Это даст вам интерфейс для реализации в вашем Proxy, и вы вызовете правильный экземпляр A в вашем InvocationHandler , используя отражение. Отражение может быть потенциальной проблемой производительности.

Из Javadoc для Прокси:

Динамический прокси-класс (просто называемый прокси-классом ниже) является класс, который реализует список интерфейсов, указанных во время выполнения, когда класс создается с поведением, как описано ниже. Прокси Интерфейс - это такой интерфейс, который реализуется прокси-классом. Прокси-экземпляр является экземпляром прокси-класса. Каждый экземпляр прокси имеет связанный объект обработчика вызова, который реализует интерфейс InvocationHandler. Вызов метода на экземпляре прокси через один из его прокси-интерфейсов будет отправлен на invoke метод обработчика вызова экземпляра, передающий прокси экземпляр, java.lang.reflect.Method объект, идентифицирующий метод который был вызван, и массив типа Object, содержащий аргументы. Обработчик вызова обрабатывает закодированный метод вызов в случае необходимости, и результат, который он возвращает, будет возвращается как результат вызова метода на экземпляре прокси.

РЕДАКТИРОВАТЬ: Если я правильно понимаю вашу проблему, у вас есть класс B, который делегирует A (или несколько As). Вы хотите использовать методы, определенные в классе B, и перенаправить их все на один метод. Это именно то, что делает прокси. Он работает с интерфейсом, но вы можете легко получить его с помощью Extract Interface в Eclipse. Прокси-сервер реализует этот интерфейс и перенаправляет все вызовы всех методов в InvocationHandler.invoke () с методом, который был вызван вместе с экземпляром объекта и параметрами, которые были использованы.

1 голос
/ 06 октября 2011

В Scala вы можете написать плагин компилятора .Я не думаю, что рефлексия может вам здесь сильно помочь.Если вам просто нужен язык JVM, который может это сделать, и это не обязательно должен быть Scala, взгляните на Groovy и его @Delegate аннотацию.

...