Access Control

We now run into the question of how public and private members behave in packages and subclasses. Think to yourself right now: when inheriting from a parent class, can we access the private members in that parent class? Or, can two classes in the same package access the other’s private members?

If you don’t know the answers right away, you can read on to find out!

Private Only code from the given class can access private members. It is truly private from everything else, as subclasses, packages, and other external classes cannot access private members. TL;DR: only the class needs this piece of code

Package Private This is the default access given to Java members if there is no explicit modifier written. Package private entails that classes that belong in the same package can access, but not subclasses! Why is this useful? Usually, packages are handled and modified by the same (group of) people. It is also common for people to extend classes that they didn’t initially write. The original owners of the class that’s being extended may not want certain features or members to be tampered with, if people choose to extend it — hence, package-private allows those who are familiar with the inner workings of the program to access and modify certain members, whereas it blocks those who are subclassing from doing the same. TL;DR: only classes that live in the same package can access

Protected Protected members are protected from the “outside” world, so classes within the same package and subclasses can access these members, but the rest of the world (e.g. classes external to the package or non-subclasses) cannot! TL;DR: subtypes might need it, but subtype clients will not

Public This keyword opens up the access to everyone! This is generally what clients of the package can rely on to use, and once deployed, the public members’ signatures should not change. It’s like a promise and contract to people using this public code that it will always be accessible to them. Usually if developers want to “get rid of” something that’s public, rather than removing it, they would call it “deprecated” instead.

TL;DR: open and promised to the world

Exercise 7.1.1 See if you can draw the access table yourself, from memory.

Have the following be the column titles: Modifier, Class, Package, Subclass, World, with the following as the Rows: public, protected, package-private, private.

Indicate whether or not each row/access type has access to that particular column’s “type”.

access

Access Control Subtleties

Default Package Code that does not have a package declaration is automatically part of the default package. If these classes have members that don’t have access modifiers (i.e. are package-private), then because everything is part of the same (unnamed) default package, these members are still accessible between these “default”-package classes.

Access is Based Only on Static Types It is important to note that for interfaces, the default access for its methods is actually public, and not package-private. Additionally, like this subtitle indicates, the access depends only on the static types.

Exercise 7.1.2

Given the following code, which lines in the demoAccess method, if any, will error during compile time?

package universe;
public interface BlackHole {
    void add(Object x); // this method is public, not package-private!
}

package universe;
public class CreationUtils {
    public static BlackHole hirsute() {
         return new HasHair();
    }
}

package universe;
class HasHair implements BlackHole {
    Object[] items;
    public void add(Object o) { ... }
    public Object get(int k) { ... }
}

import static CreationUtils.hirsute;
class Client {
   void demoAccess() {
      BlackHole b = hirsute();
      b.add("horse");
      b.get(0);
      HasHair hb = (HasHair) b;
   }
}

ANSWER

  • b.get(0); This line errors because b is of static type BlackHole, but the BlackHole interface does not define a get method! Even though you and I both know that b is dynamically a HasHair, and thus has the get method, the compiler bases its checks off the static type.

  • HasHair hb = (HasHair) b; This one is tricky, but notice that the HasHair class is not a public class - it's package-private. This means that Client, a class outside of the universe package, can't see that the HasHair class exists.

Object Methods

Objects Class All classes are hyponyms of Object. This means that all classes will inherit the following methods:

  • String toString()
  • boolean equals(Object obj)
  • Class<?> getClass()
  • int hashCode()
  • protected Object clone()
  • protected void finalize()
  • void notify()
  • void notifyAll()
  • void wait()
  • void wait(long timeout)
  • void wait(long timeout, int nanos)

Only the bolded methods will be discussed or used in CS61B, however!

.toString() If you want to provide a custom String representation of an object (similar to the idea of _repr_ or __str\ in Python), you can override this method with your own implementation.

.equals() vs. == == will always check checks if two variables reference the same object (looks at the address bits in box). This is equivalent to checking if “object1 is object2”.

By default, .equals() will also have the same functionality as ==, and checks for the address bits in the box. However, you have the ability to override the .equals() method for your own class to create a custom .equals() that behave more semantically.

An example would be with the following:

public static void main(String[] args) {
    int[] x = new int[]{0, 1, 2, 3, 4};
    int[] y = new int[]{0, 1, 2, 3, 4};
    System.out.println(x == y);
    System.out.println(x.equals(y));
}

The first print statement would be false, as x and y are pointing to two different places in memory. However, the second print statement would be true, as Array.equals checks for the contents of the given arrays, and not just the address bits in each variable’s box.

Rules for Equals in Java When overriding a .equals() method, it may sometimes be trickier than it seems. A couple of rules to adhere to while implementing your .equals() method are as follows:

1.) equals must be an equivalence relation

  • reflexive: x.equals(x) is true
  • symmetric: x.equals(y) IFF y.equals(x)
  • transitive: x.equals(y) and y.equals(z) implies x.equals(z)

2.) It must take an Object argument, in order to override the original .equals() method

3.) It must be consistent

  • if x.equals(y), then as long as x and y remain unchanged: x must continue to equal y

4.) It is never true for null

  • x.equals(null) must be false

results matching ""

    No results matching ""