tags:

views:

83

answers:

5

iI have some trouble getting sth like this to work in 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() {
        // ...
    }
}

say i have these classes where my "main" class ("A") has several members. these pointers to other objects ("B", "C") are made static so code in "B" can access (non-static) methods in "C".

i know that static methods can only access other static methods and/or variables, but since the REFERENCE to these classes is static - and not the actual class itself - it should work fine this way - shouldn't it?

i'm a bit stuck here, since this kind of access crashes my application all the time :(

i need ClassB to access (non-static) methods in ClassC without passing all the references to A or C along. how do i do this?

+5  A: 

Well, other than your constructor for ClassA being malformed (you're missing the brackets), that code compiles... but until you construct an instance of ClassA, both PointerToB and PointerToC will be null. If you want to just initialize them once, do so in static initializers - e.g. with the declarations:

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

Even so, there are two nasty bits of design here (IMO):

  • Public mutable fields are almost always a mistake
  • If everything uses ClassA.PointerToB and ClassA.PointerToC, you've effectively got singletons, with the accompanying downsides. Consider dependency injection instead.
Jon Skeet
+1 for the bullet points at the end.
Andrzej Doyle
A: 

i need ClassB to access (non-static) methods in ClassC without passing all the references to A or C along. how do i do this?

Then ClassB needs a reference to an instance of C - no real way around this.

Instead of ClassB accessing C via a static reference in A, why not write B a little differently?

public class ClassB {
    public void doSthB(ClassC c) {
        c.doSthC();
    }
}
matt b
A: 

This cannot ever work. static means that different instances of a class can never have different values for a field, and yet that is exactly what you say you want to happen. Instead of making PointerToB static, provide the objects you use directly with references to the collaborators they need.

(You also seem confused about the difference between classes and objects. Try to give a non-generic example to receive more useful help here!)

Kilian Foth
+3  A: 

When you say "you're stuck", do you mean you're getting NullPointerExceptions?

The variables PointerToB and PointerToC are static and will indeed be set by ClassA's constructor. However, this will only be run when an instance of ClassA is created.

What you were probably thinking of instead is a static initialiser block, which will be run when the class is loaded:

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

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

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

Now whenever you access these variables they will be non-null. In this particular case, since you're not doing anything complicated, you may as well initialise them in the declaration:

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

This is functionally identical, but is arguably easier to write and understand.


Edit: As an aside, note that your code is actually quite nasty as it is now - there's a hidden temporal dependence, in that it would appear to work correctly so long as some bit of code constructed an instance of ClassA before you accessed the static variables. This could have worked quite happily (by coincidence) for months, before someone makes some tiny change (e.g. create a ClassA on demand rather than eagerly) and BANG, all of a sudden some completely unrelated code starts breaking.

Even worse than this temporal dependency, you're opening yourself up to race conditions that you probably hadn't even considered. Both of my versions above will guarantee that the static variables are initialised before you can do anything in the class, but this isn't the case with your version. You're setting them on each and every time that the class is constructed. If one thread's calling the constructor while another thread is accessing the variable, the results are undefined and could manifest as "random" crashes, weird data issues or other things.

Even if you avoid this, the actual object references will change over time, so you might call something like PointerToC.doSthC() (let's say it increments a counter in C) - this will correctly make the counter 1, but at a later point in the program, PointerToC has been reset to a new instance of ClassC and the counter is back at zero again!

I don't mean to show you up or anything by this, merely to point out how it's important to think about data consistency issues - and how just because something compiles, and in fact even if it works (right now) it's not necessarily correct.

Andrzej Doyle
temporal dependency: theres no access to B or C by any other class or method than by A - and A initializes B and C during its own init.
xenonite
@xenonite - **not right now** there's not. If you're doing something useful, you can guarantee that someone else will need to do something similar in future, and the sensible way is to reuse your class. My comments are *all* along the lines that this will work right now, but does not behave "nicely" as part of a project, and in that respect is incredibly fragile with regards to change. Which is the kiss of death for any serious software.
Andrzej Doyle
A: 

I think the solution you're looking for is the singleton pattern:

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;
    }

}

that way, the classes are decoupled from each other (a bit), and you can access them independently:

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

However, Singletons are evil, so be careful what you are doing. (But what you are doing now is arguably even more evil)

seanizer