MIT 6.031 Reading 2: Static Checking

MIT 6.031

Reading 2: Static Checking

Data Types

  • primitive types:
    • numeric types (double, int, char)
    • boolean
    • double has more precision than float

the values of primitive types are immutable and are not instance of classes

  • reference types are classes
    • strings
    • arrays
    • lists
    • maps

Thus, any object or instance of a class is of a reference type

  • primitive types are conventionally lowercase; object types are conventionally capitalized
  • object types can contain internal structures (references to other primitives or objects); primitive types have no internal structures
  • a primitive value uses a small, fixed amount of memory; an object value may use varying, sometimes large amounts of memory

Exercise

int a = 5;
int b;
if (a > 10) {
    b = 2;
} else {
    // b = 4;
}
b *= 3;

answer:
We will receive an error from the Java compiler, before we run the program
explanation:
In addition to requiring that variables be declared, the Java compiler must also be certain that every variable has been assigned a value before we attempt to access its value.

For this code, the compiler sees that b is assigned in only one branch of the if-else, and therefore b might not have been assigned any value when we reach the last line.

Snapshot diagrams

  • stack: methods in progress and their local variables
  • heap: objects that currently exist

Mutating values vs. reassigning variables

reassignment and immutable values

String is an example of an immutable type, whenever we change any string, a new instance is created

String s = "abc";
s = s + "def";
// will work => s = abcdef, a new instance

Mutable values

By contrast, StringBuilder is a mutable object

StringBuider sb = new StringBuilder("a");
sb.append("b");

Unreassignable references

final keyword makes a reference unreassignable

final int n = 5;
  • => gives compile error if compiler not convinced that your final variable wiil only be assigned once at runtime
  • note we can have an unreassignable reference to a mutable value
  • we can also have an reassignable reference to an immutable value

Exercise (need review!!!)

void f(String s, StringBuilder sb) {
  s.concat("b");
  s + = "c";
  sb.append("d");
}

and

String t = "a";
StringBuilder tb = new StringBuilder(t);
f(t, tb);

after this code runs,
t = a
tb = ad

String t = "a"; declares a new variable t points to a String object "a". This object is immutable.
Then f(t, tb) calls instantiates new parameter variables s pointing to "a".
Then due to s.concat("b");, a new object "ab" is created and returns it. But because the return value of the call is not assigned to anything, it has no affect on s.
Then s + = "c" creates a new String object "ac", and now s points to "ac". < strong>The variable t is not affected by this assignment

It’s essential to realize that in Java, reassigning a parameter inside a method DOES NOT reassign a variable in the caller. This is call-by-value.

The only way to communicate back to caller through a parameter is to mutate the object.

== vs. equals()

  • the == operator compares the values of primitives
  • the .equals() method compares the values of objects
  • when used on object types, == tests wether the two experiments refer to the same object in memory
String s = "foobar";
String t = "foobar";
if (s == t) { ... }

answer:
s == t might return true, because Java might cleverly reuse the same String object for both s and t
explanation:

Sometimes Java is too clever, at the cost of safety from bugs. Java keeps a hash-indexed pool of string objects, particularly to save memory space for literal strings, like "foobar" here.

Java reuses an existing String object from the pool when it sees the same literal string in your code. As a result, you may find that an expression that simply compares literals, like "foobar" == "foobar", returns true! This doesn’t mean that == is actually comparing the individual characters of the strings. It just means that "foobar" on the righthand side happened to be exactly the same object as "foobar" on the lefthand side, because it was reused from the pool.

Java generally doesn’t consult this pool of objects when manipulating strings at runtime. So "FOOBAR".toLowerCase() == "foobar" returns false – probably. The implementation of toLowerCase() may opt to reuse an existing "foobar" from the pool, rather than creating a fresh string object. Its spec doesn’t say one way or the other.

Java Collections

Lists, Sets, and Maps

  • List
    • int cnt = lst. size();
    • lst.add(e);
    • if (lst. isEmpty()) ...
    • lst. contains(e)
  • Map
    • keys must be hashable
    • map. put(key, val)
    • map.get(key)
    • map. containsKey(key)
    • map. remove(key)
  • Set
    • a set is an unordered collection of zero or more unique objects
    • objects must be hashable
    • s1. contains(e)
    • s1. containsAll(s2)
    • s1. removeAll(s2)

Literals

String[] arr = {"a", "b", "c"};
List.of("a", "b", "c"); // it's immutable!!!
Set.of("a", "b", "c"); // it's immutable!!!
Map.of("apple", 5, "orange", 7); // it's immutable!!!

Generics: declaring List, Set, and Map variables

List<String> cities;
Set<Integer> numbers
Map<String, Turtle> turtles;
cities.add("Dallas");
String city = cities. get(0);

ArrayLists and LinkedLists: creating Lists

List, Set, and Map are all interfaces: they define how these respective types work, but they don’t provide implementation code.

// will produce a mutable list!!!
List<String> firstNames = new ArrayList<String>(List. of("Kawhi", "Kevin", "Kyrie"));
List<String> lastNames = new LinkedList<>(Set.of("Leonard", "Durant", "Irving"));

HashSets and HashMaps: creating Sets and Maps

Set<Integer> numbers = new HashSet<>();
Map<String, Turtle> turtles = new HashMap<>();
  • Treeset : sorted
  • TreeMap
  • if key does not exist in map, then map.get(key) will returns null, but in C++, it will get value 0

Iteration

List<String> cities = new ArrayList<>();
Set<Integer> numbers = new HashSet<>();
Map<String,Turtle> turtles = new HashMap<>();
for (String city : cities) {
    System.out.println(city);
}

for (int num : numbers) {
    System.out.println(num);
}

for (String key : turtles. keySet()) {
    System.out.println(key + ": " + turtles.get(key));
}

Enumerations

Sometimes a type has a small, finite set of immutable values, such as

  • months
  • days of the week
  • compass points
  • available colors
public enum Month {
    JANUARY, FEBRUARY, MARCH, APRIL,
    MAY, JUNE, JULY, AUGUST,
    SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER;
}

public enum PenColor {
    BLACK, GRAY, RED, PINK, ORANGE,
    YELLOW, GREEN, CYAN, BLUE, MAGENTA;
}
PenColor drawingColor;
drawingColor = PenColor.RED;
int month = TUESDAY; // no error if integers are used
Month month = DayOfWeek. TUESDAY; // static error if enumerations are used