У меня проблема со скрытием имени, которую очень сложно решить. Вот упрощенная версия, объясняющая проблему:
Есть класс: org.A
package org;
public class A{
public class X{...}
...
protected int net;
}
Тогда есть класс net.foo.X
package net.foo;
public class X{
public static void doSomething();
}
А теперь вот проблемный класс, который наследуется от A
и хочет вызватьnet.foo.X.doSomething()
package com.bar;
class B extends A {
public void doSomething(){
net.foo.X.doSomething(); // doesn't work; package net is hidden by inherited field
X.doSomething(); // doesn't work; type net.foo.X is hidden by inherited X
}
}
Как видите, это невозможно. Я не могу использовать простое имя, X
потому что оно скрыто унаследованным типом. Я не могу использовать полное имя net.foo.X
, потому что net
оно скрыто унаследованным полем.
В B
моей кодовой базе есть только класс ; классы net.foo.X
и org.A
являются библиотечными, поэтому я не могу их изменить!
Мое единственное решение выглядит так: я мог бы вызвать другой класс, который в свою очередь вызывает X.doSomething()
; но этот класс будет существовать только из-за конфликта имен, что кажется очень запутанным! Нет ли раствор , в котором я могу напрямую позвонить X.doSomething()
из B.doSomething()
?
На языке, который позволяет указывать глобальное пространство имен, например, global::
в C # или ::
C ++, я мог бы просто использовать префикс net
этого глобального префикса, но Java этого не позволяет.
источник
public void help(net.foo.X x) { x.doSomething(); }
и позвонить сhelp(null);
net.foo.X
есть метод, нетorg.A.X
!A
? Наследование может быть очень неприятным, как вы уже выяснили…I could call another class that in turn calls X.doSomething(); but this class would only exist because of the name clash, which seems very messy
+1 за отношение чистого кода. Но мне кажется, что в этой ситуации вам следует пойти на компромисс. Просто сделайте это и напишите хороший длинный комментарий о том, почему вам пришлось это сделать (возможно, со ссылкой на этот вопрос).Ответы:
Вы можете привести
null
к типу, а затем вызвать для него метод (который будет работать, поскольку целевой объект не участвует в вызове статических методов).Это имеет преимущества
net.foo.X
),B
имя, которое хотите, поэтому aimport static
не будет работать в вашем конкретном случае),Обратной стороной является то, что этот код действительно ужасен! Для меня это генерирует предупреждение, и это в целом хорошо. Но поскольку он работает с проблемой, которая в противном случае совершенно непрактична, добавление
в подходящей (минимальной!) точке включения компилятор выключится.
источник
doSomething
в вашей иерархии есть метод), так что да, лучшее решение.Вероятно, самый простой (не обязательно самый простой) способ справиться с этим - использовать класс делегата:
а потом ...
Это несколько многословно, но очень гибко - вы можете заставить его вести себя так, как хотите; плюс он работает примерно одинаково как с
static
методами, так и с объектамиПодробнее об объектах делегатов здесь: http://en.wikipedia.org/wiki/Delegation_pattern
источник
Вы можете использовать статический импорт:
Остерегайтесь этого
B
иA
не используйте методы с именемdoSomething
источник
doSomething
в качестве названия метода вB
…doSomething
место в иерархии наследования (из-за того, как решаются цели вызова методов). Так что Кнут поможет вам, если вы хотите вызватьtoString
метод. Решение, предложенное Доналом, - это то, что есть в книге Блоха о Java Puzzlers (я думаю, что не искал его), поэтому мы можем считать его действительно авторитетным ответом :-)Правильный способ сделать это - статический импорт, но в самом худшем случае вы МОЖЕТЕ создать экземпляр класса, используя отражение, если вы знаете его полное имя.
Java: новый экземпляр класса, не имеющий конструктора по умолчанию
А затем вызовите метод экземпляра.
Или просто вызовите сам метод с отражением: вызов статического метода с использованием отражения
Конечно, это явно последние средства.
источник
static import
фича добавили только вJava 1.5
. Я не завидую людям, которым нужно разрабатывать под 1.4 и ниже, мне пришлось однажды и это было ужасно!((net.foo.X)null).doSomething()
решение, которое работает в старой java. Но если типA
даже содержит внутренний типnet
, ТО это единственный ответ, который остается действительным :).Не совсем ответ, но вы можете создать экземпляр X и вызвать на нем статический метод. Это был бы способ (грязный, я признаю) вызвать ваш метод.
источник
null
к типу и последующий вызов метода для него было бы лучше, поскольку создание объекта может иметь побочные эффекты, которых я не хочу.null
, казалось бы, была одним из наиболее чистых способов сделать это. Должен@SuppressWarnings("static-access")
быть без предупреждений…null
, но кастингnull
всегда был для меня разбитым сердцем.Нет необходимости выполнять приведение или подавлять какие-либо странные предупреждения или создавать какой-либо избыточный экземпляр. Просто уловка, использующая тот факт, что вы можете вызывать статические методы родительского класса через подкласс. (Подобно моему хакерскому решению здесь .)
Просто создайте такой класс
(Частный конструктор в этом последнем классе гарантирует, что никто не сможет случайно создать экземпляр этого класса.)
Тогда вы можете позвонить
X.doSomething()
через него:источник
Что, если вы попытаетесь получить глобальное пространство имен, учитывая, что все файлы находятся в одной папке. ( http://www.beanshell.org/javadoc/bsh/class-use/NameSpace.html )
источник
getGlobal()
не является стандартным методом Java для пакетов ... (я думаю, что у пакетов не может быть никаких методов в Java ...)Это одна из причин, по которой композиция предпочтительнее наследования.
источник
Я бы использовал паттерн Стратегия.
Теперь вы также развязали свою зависимость, поэтому ваши модульные тесты будет проще писать.
источник
implements SomethingStrategy
вXSomethingStrategy
объявление класса.