简体   繁体   中英

How do you ensure a class is instantiated only from within a specific class?

Let's say I have a ParkingMeter class and a ParkingSlip class. ParkingSlips should only be created from within the ParkingMeter class, but ParkingSlips should be accessible by an external class called Car (so you can check if each car has paid for parking).

Is there a way to ensure ParkingSlips can only be created from within ParkingMeter but is still accessible to other classes?

If you don't want to make ParkingSlip an inner class, put the two classes in the same package and make ParkingSlip 's constructor package-private. It won't stop other classes in the same package from creating parking slips, but it will stop evildoers in other packages.

package parking;

public class ParkingSlip {
    ParkingSlip() {
    }
}

public class ParkingMeter {
    public ParkingSlip getSlip() {
        return new ParkingSlip();
    }
}

The safest way is to make ParkingSlip public inner static class of ParkingMeter and give it a private constructor.

public class ParkingMeter {
    public static class ParkingSlip {
        private ParkingSlip() {
        }
    }
}

A less safe, but more useable, way is to give default (aka "package") visibility to the constructor and have ParkingMeter in the same package, but any other class from the same package - even from a different project - will have access to the constructor.

Note that whichever way you go, reflection can circumvent it.

Actually there is a way to check who called a constructor or method:
by examining the current stack trace .
Not that I'd recommend it, but hey, it works:

public ParkingSlip() {

    // get the current stack trace
    // element 0: java.lang.Thread.getStackTrace
    // element 1: this constructor
    // element 2: the class/method calling this constructor
    StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();

    // verify that it was indeed the ParkingMeter who invoked the constructor 
    if (!ParkingMeter.class.getName().equals(stackTrace[2].getClassName())) {
        throw new IllegalAccessError("Restricted access, only ParkingMeter is allowed to create instances");
    }
}

A cleaner solution , which resticts constructor access at compile time (the solution above only checks at runtime) is to

  • place ParkingMeter and ParkingSlip (only those two) in a dedicated package
  • leave both classes public
  • make the constructor(s) for ParkingSlip 'friendly' (neither private , nor protected , nor public )

This ensures that the ParkingSlip constructors can only be accessed by classes in the same package, and as ParkingMeter is the only other class in that package, you've got what you want.

public class ParkingSlip {

   // 'friendly' constructor
   ParkingSlip() {
   }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM