Подтвердить что ты не робот

Скрытие Java-имени: жесткий путь

У меня проблема с скрытием имени, которую очень сложно решить. Вот упрощенная версия, которая объясняет проблему:

Существует класс: 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:: в С# или :: в С++, я мог бы просто префикс net с этим глобальным префиксом, но Java этого не допускает.

4b9b3361

Ответ 1

Вы можете применить тип null к типу, а затем вызвать метод на нем (который будет работать, поскольку целевой объект не участвует в вызове статических методов).

((net.foo.X) null).doSomething();

Это имеет преимущества

  • является побочным эффектом (проблема с экземпляром net.foo.X),
  • не требуя переименования чего-либо (чтобы вы могли указать метод в B имя, которое вы хотите его иметь; поэтому import static не будет работать в вашем конкретном случае),
  • не требуется введение класса делегата (хотя это может быть хорошей идеей...) и
  • не требует накладных расходов или сложности работы с API отражения.

Недостатком является то, что этот код действительно ужасен! Для меня это порождает предупреждение, и это хорошо в целом. Но поскольку он работает над проблемой, которая в противном случае полностью непрактична, добавив

@SuppressWarnings("static-access")

в подходящей (минимальной!) охватывающей точке закроет компилятор.

Ответ 2

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

import net.foo.X;
class C {
    static void doSomething() {
         X.doSomething();
    }
}

а затем...

class B extends A {
    void doX(){
        C.doSomething();
    }
}

Это несколько подробный, но очень гибкий - вы можете заставить его вести себя так, как вы хотите; плюс он работает почти таким же образом как с методами static, так и с экземплярами объектов

Подробнее о объектах делегата здесь: http://en.wikipedia.org/wiki/Delegation_pattern

Ответ 3

Вы можете использовать статический импорт:

import static net.foo.X.doSomething;

class B extends A {
    void doX(){
        doSomething();
    }
}

Следите, чтобы B и A не содержали методов с именем doSomething

Ответ 4

Правильный способ делать вещи - это статический импорт, но в абсолютном худшем случае вы МОЖЕТЕ построить экземпляр класса с использованием отражения, если знаете его полное имя.

Java: newInstance класса, который не имеет конструктора по умолчанию

И затем вызовите метод в экземпляре.

Или просто вызовите сам метод с отражением: Вызов статического метода с использованием отражения

Class<?> clazz = Class.forName("net.foo.X");
Method method = clazz.getMethod("doSomething");
Object o = method.invoke(null);

Конечно, это, очевидно, последние курорты.

Ответ 5

Не совсем ответ, но вы можете создать экземпляр X и вызвать на нем статический метод. Это будет способ (грязный, я допускаю), чтобы вызвать ваш метод.

(new net.foo.X()).doSomething();

Ответ 6

Нет необходимости делать какие-либо броски или подавлять любые странные предупреждения или создавать лишние экземпляры. Просто трюк, используя тот факт, что вы можете вызвать статические методы родительского класса через подкласс. (Подобно моему хакерскому решению здесь.)

Просто создайте класс, подобный этому

public final class XX extends X {
    private XX(){
    }
}

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

Затем вы можете вызвать X.doSomething() через него:

    public class B extends A {

        public void doSomething() {
            XX.doSomething();
        }

Ответ 7

Что делать, если вы попытаетесь получить gobalnamespace, если все файлы находятся в одной папке. (http://www.beanshell.org/javadoc/bsh/class-use/NameSpace.html)

    package com.bar;
      class B extends A {

       public void doSomething(){
         com.bar.getGlobal().net.foo.X.doSomething(); // drill down from the top...

         }
     }

Ответ 8

Это одна из причин, по которой композиция предпочтительнее наследования.

package com.bar;
import java.util.concurrent.Callable;
public class C implements Callable<org.A>
{
    private class B extends org.A{
    public void doSomething(){
        C.this.doSomething();
    }
    }

    private void doSomething(){
    net.foo.X.doSomething();
    }

    public org.A call(){
    return new B();
    }
}

Ответ 9

Я бы использовал шаблон стратегии.

public interface SomethingStrategy {

   void doSomething();
}

public class XSomethingStrategy implements SomethingStrategy {

    import net.foo.X;

    @Override
    void doSomething(){
        X.doSomething();
    }
}

class B extends A {

    private final SomethingStrategy strategy;

    public B(final SomethingStrategy strategy){
       this.strategy = strategy;
    }

    public void doSomething(){

        strategy.doSomething();
    }
}

Теперь вы также отделили свою зависимость, поэтому ваши модульные тесты будут легче писать.