Как получить доступ к нестатическим методам в объектах, когда ссылка на этот объект является статической? - PullRequest
0 голосов
/ 01 июля 2010

У меня возникли проблемы с тем, чтобы что-то подобное работало в Java:

public class ClassA {
    public static ClassB PointerToB;
    public static ClassC PointerToC;

    public ClassA() {
        PointerToB = new ClassB();
        PointerToC = new ClassC();

        PointerToB.doSthB();
    }
}

public class ClassB {
    public void doSthB() {
        ClassA.PointerToC.doSthC();
    }
}

public class ClassC {
    public void doSthC() {
        // ...
    }
}

скажем, у меня есть эти классы, где у моего "основного" класса ("А") есть несколько членов. эти указатели на другие объекты ("B", "C") сделаны статическими, поэтому код в "B" может обращаться к (нестатическим) методам в "C".

Я знаю, что статические методы могут получить доступ только к другим статическим методам и / или переменным, но, поскольку ССЫЛКИ на эти классы являются статическими, а не самим классом, они должны нормально работать, не так ли? *

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

Мне нужен ClassB для доступа к (нестатическим) методам в ClassC без передачи всех ссылок на A или C. как мне это сделать?

Ответы [ 5 ]

5 голосов
/ 01 июля 2010

Ну, кроме вашего конструктора для ClassA, который искажен (вам не хватает скобок), этот код компилирует ... но пока вы не создадите экземпляр ClassA, оба PointerToB и PointerToC будет нулевым. Если вы хотите просто инициализировать их один раз, сделайте это в static initializer - например, с декларациями:

public static ClassB PointerToB = new ClassB();
public static ClassC PointerToC = new ClassC();

Несмотря на это, здесь есть две неприятные особенности (ИМО):

  • Открытые изменяемые поля почти всегда являются ошибкой
  • Если все используют ClassA.PointerToB и ClassA.PointerToC, у вас фактически есть синглтоны с сопутствующими недостатками. Вместо этого рассмотрим внедрение зависимостей.
3 голосов
/ 01 июля 2010

Когда вы говорите «вы застряли», вы имеете в виду, что вы получаете NullPointerException с?

Переменные PointerToB и PointerToC являются статическими и действительно будут установлены конструктором ClassA. Однако это будет выполняться только при создании экземпляра ClassA.

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

public class ClassA {
    public static ClassB PointerToB;
    public static ClassC PointerToC;

    static {
        PointerToB = new ClassB();
        PointerToC = new ClassC();
    }

    public ClassA() {
        PointerToB.doSthB();
    }
}

Теперь, когда вы будете обращаться к этим переменным, они будут отличны от NULL. В данном конкретном случае, поскольку вы не делаете ничего сложного, вы можете также инициализировать их в объявлении:

public class ClassA {
    public static ClassB PointerToB = new ClassB();
    public static ClassC PointerToC = new ClassC();
}

Это функционально идентично, но, возможно, легче написать и понять.


Edit: В дополнение, обратите внимание, что ваш код на самом деле довольно неприятный, как сейчас - есть скрытая временная зависимость, в которой он будет работать правильно, пока какой-то фрагмент кода создает экземпляр ClassA перед вами Доступ к статическим переменным. Это могло бы сработать довольно счастливо (по совпадению) в течение нескольких месяцев, прежде чем кто-то внесет какое-то крошечное изменение (например, создаст ClassA по требованию, а не с нетерпением) и BANG, внезапно какой-то совершенно не связанный код начнет разрушаться.

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

Даже если вы избегаете этого, фактические ссылки на объекты со временем изменятся, поэтому вы можете вызвать что-то вроде PointerToC.doSthC() (скажем, увеличивает счетчик в C) - это правильно сделает счетчик 1, но позже точка в программе, PointerToC была сброшена на новый экземпляр ClassC и счетчик снова обнулен!

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

0 голосов
/ 01 июля 2010

Я думаю, что решение, которое вы ищете, это шаблон синглтона:

public class ClassA{

    private static final class InstanceHolder{

        private static final ClassA INSTANCE = new ClassA();

        private InstanceHolder(){
        }

    }

    public static ClassA getInstance(){
        return InstanceHolder.INSTANCE;
    }

}

public class ClassB{

    private static final class InstanceHolder{

        private static final ClassB INSTANCE = new ClassB();

        private InstanceHolder(){
        }

    }

    public static ClassB getInstance(){
        return InstanceHolder.INSTANCE;
    }

}

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

// inside ClassA
public void doSthB() {
    ClassB.getInstance().doSthC();
}

Однако, Синглтоны - это зло , поэтому будьте осторожны с тем, что вы делаете. (Но то, что вы делаете сейчас, возможно, еще более зло)

0 голосов
/ 01 июля 2010

Это не может работать никогда. static означает, что разные экземпляры класса никогда не могут иметь разные значения для поля, и все же это именно то, что вы говорите, что хотите, чтобы это произошло. Вместо того, чтобы делать PointerToB статическим, предоставьте объектам, которые вы используете, ссылки на нужные им соавторы.

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

0 голосов
/ 01 июля 2010

Мне нужен ClassB для доступа к (нестатическим) методам в ClassC без передачи всех ссылок на A или C.как мне это сделать?

Тогда ClassB нужна ссылка на экземпляр C - никакого реального способа обойти это.

Вместо доступа ClassB к C через статическую ссылку в Aпочему бы не написать B немного по-другому?

public class ClassB {
    public void doSthB(ClassC c) {
        c.doSthC();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...