Semantic Mysteries
The project repository for this week’s labs can be found at:
Make sure to create a new repository from this template repository on your account (“Use this template” → “Create a New Repository”) and add your partner(s) as collaborators. Remember to periodically commit and push your work throughout the week!
Part 1: Puzzling Through Problems
In this lab, we explore the semantics of objects in the context of Java. That is, we will integrate the object—a programming entity with state and behavior—into our mental model of computation. By doing so, we can answer some critical questions about how programs with objects execute in certain unintuitive settings as well as explain some bits of Java syntax that we have glossed over until this point.
These are all questions that have “obvious” book answers, but they require a bit of effort on your part to internalize what they mean with respect to your programs. Try to (a) answer the question in your own words and (b) give an example illustrating your answer, e.g., example code snippets demonstrating the differences between two different situations. You should feel confident enough in your answer to explain this concept to someone that is new to Java!
Problem 1.1: Value versus Reference Semantics
What is the difference in calling the following three change methods, and why is this the case?
// in Cell.java
public class Cell {
public int x;
public Cell(int x) { this.x = x; }
public static void change1(int x) {
x = 5;
}
public static void change2(Cell c) {
c.x = 5;
}
public static void change3(Cell c) {
c.x = 5;
c = new Cell(0);
}
}
What’s the rule here? Does java pass parameters by value (i.e., copy) or by reference? Is passing an object (with an arbitrary number of fields) to a function more costly than passing a primitive?
Problem 1.2: The this Variable
What is the this variable in a method, and where does it come from?
How do these four classes differ with respect to their increment methods?
// In Counters.java
class Counter1 {
public int value;
public void increment() {
value += 1;
}
}
class Counter2 {
public int value;
public void increment(int value) {
value += value;
}
}
class Counter3 {
public int value;
public void increment(int value) {
this.value += value;
}
}
class Counter4 {
public int value;
public void increment(int value) {
value += this.value;
}
}
What’s the rule for variable look-up in Java? How does this differ from function calls in C?
Problem 1.3: static Versus Non-static Members
What is the distinction between a static and non-static member (i.e., field or method)?
In particular, imagine using this variant of a counter:
// In Static.java
class Counter {
public static int value;
public Counter() {
value = 0;
}
public void increment(int value) {
this.value += value;
}
}
And why does this code not work? How do you fix it?
In general, what is the rule for mixing static and non-static things?
Does this code work?
Why or why not?
// In static.java
public class Static {
public void printGreeting() {
System.out.println("Hello World!");
}
public static void main(String[] args) {
printGreeting();
}
}
Problem 1.4: Reference Versus Structural Equality
Does the following code snippet behave as you expect? Why? How do you fix its behavior?
// In Ref.java
public class Counter {
public int value;
public Counter() { this.value = 0; }
public void increment() { this.value += 1; }
public static void main(String[] args) {
Counter c1 = new Counter();
Counter c2 = new Counter();
System.out.println("Are c1 and c2 equal? " + (c1 == c2));
}
}
With this in mind, does this code behave as you expect?
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2);
How about this snippet? What’s the difference between these two snippets and why?
Scanner in = new Scanner(System.in);
String s3 = in.nextLine(); // Suppose the user types the same
String s4 = in.nextLine(); // line for both s3 and s4...
System.out.println(s3 == s4);
Part 2: Assertions
In these problems, you’ll work your ability to reason about program properties and whether they are preserved throughout execution. Knowing whether a property is preserved within a program is a cornerstone of efficient debugging as we will discuss in the coming days.
Problem 2.1
Consider the following method:
public static int foo1(int x1, int x2) {
// Point A
if (x1 < 0 || x2 < 0) {
return -1;
}
// Point B
int y1 = x1 % 10;
int y2 = x2 % 10;
int z = 0;
// Point C
if (y1 < y2) {
z = y1 - y2;
} else {
z = y2 - y1;
}
// Point D
return z;
}
Now, consider whether each of the propositions holds at the given point in the method all the time (✓), some of the time (?), or none of the time (✗):
- Point A:
x1 == 0x2 < 0
- Point B:
x1 == 0x2 < 0
- Point C:
y1 < 5y2 > 0
- Point D:
z > y1z < 0
Problem 2.2
public static String foo2(String s, char c) {
String ret = "";
// Point A
if (s == null) { throw new IllegalArgumentException(); }
if (s.length() < 2) { return s; }
// Point B
for (int i = 1; i < s.length(); i++) {
// Point C
ret += s.charAt(i);
ret += c;
// Point D
}
// Point E
return ret;
}
Now, consider whether each of the propositions holds at the given point in the method all the time (✓), some of the time (?), or none of the time (✗):
s.length >= 2 | ret.length() > 0 | ret.length() % 2 == 0 | |
|---|---|---|---|
| Point A | |||
| Point B | |||
| Point C | |||
| Point D | |||
| Point E |