Object-oriented programming objects aren’t objects

Why Java and C# objects conform better to an obscure 17th-century philosophical account of substance than they do to common sense

Jacob W. Archambault
9 min readMay 11, 2019

Object-oriented programming is a computing paradigm implemented in widely used programming languages like Java and C#. It is often described colloquially in terms of the following intuitively plausible ideas:

  • The most basic beings in the universe are particular objects
  • Objects belong to kinds, mathematically represented as classes
  • Objects have states and behavior, which are represented mathematically by fields and methods.

States and behavior can only belong to things, which themselves belong — in a different sense — to kinds. To enforce this, object-oriented programming requires fields and methods for kinds of objects to be attached to an associated class. This is usually achieved by placing the code for the methods and fields an object of a given class may have within brackets {} that give the code for the class as a whole. When this happens, fields and methods are said to be encapsulated in their class.

Since objects belonging to one class can belong to a second class by virtue of belonging to the first, as e.g. whatever is a chihuahua is thereby a dog, objects in one class are able to inherit the behavior of other classes — if there are any behaviors or characteristics that all dogs have in common, chihuahuas will also have them.

The principles specifying the above general ideas in ways appropriate for programming languages are called encapsulation and inheritance, and these are two of the basic principles of every object-oriented programming language.

Once one gets past the analogies, however, one realizes that very little object-oriented programming actually conforms to the pre-theoretical explanation given above. To explain this, it will be useful to take a look at a very simple class from an object-oriented language, Java.

1 Object-oriented programming objects aren’t objects

class Bicycle {

int cadence = 0;
int speed = 0;
int gear = 1;

void changeCadence(int newValue) {
cadence = newValue;
}

void changeGear(int newValue) {
gear = newValue;
}

void speedUp(int increment) {
speed = speed + increment;
}

void applyBrakes(int decrement) {
speed = speed - decrement;
}

void printStates() {
System.out.println("cadence:" +
cadence + " speed:" +
speed + " gear:" + gear);
}
}

The above is an example of a bicycle class taken from the Java Tutorials, a series of tutorials for learning the Java language put out by Oracle, who have held the rights to the language since their acquisition of Sun Microsystems, the language’s creator, in 2010. The class’ fields are cadence, speed, and gear. In this case, the fields do seem to fit into a (non-exhaustive) list of properties that a bicycle can have.

The class’ methods, however, don’t reflect the pre-theoretical intuition at all. Instead of representing behaviors of the object itself, these methods represent ways that the fields of an object can themselves be changed or acted on: the changeCadence() method, for instance, doesn’t reflect an action performed by the bike itself, but one that the bike’s rider could perform to it. Worse, the printStates() method, which prints the bicycle’s cadence, speed, and gear to a terminal, doesn’t reflect anything that someone could do with an actual bicycle. Instead, it represents a task that a program or user can perform on an instance of the bicycle class understood precisely as a digital object in digital space.

In short, real-world objects have states and behavior. Objects in object-oriented programming (OOP) have analogues to these called fields and methods. The fields of object-oriented objects parallel the states of real-world objects more or less faithfully. But the parallel between the methods of object-oriented objects and the behavior of real-world objects breaks down relatively quickly, for two reasons. The first is that more often than not, the methods of object oriented objects do not represent things the object can do, but things that can be done to the object. Second, objects in object-oriented languages don’t neatly distinguish between methods that act on the object in accordance with the nature of the object it is intended to represent (e.g. as pedal() method might work on a bicycle object), and methods that act on the object precisely as a bundle of code within a computer program. Instead, there is a blending together of larger reality that the object is meant to represent with that which it is, as a piece of code, actually a part of.

2 What are OOP objects?

What, then, are the objects of object-oriented programming? Better yet, what is it that makes such objects unified in some way, rather than random bundles of code? And accordingly, what is it that makes some OOP objects better conform to the ideal of an OOP object than others?

To answer this question, let’s look at one more example:

public class Card {
private final int rank;
private final int suit;

// Kinds of suits
public final static int DIAMONDS = 1;
public final static int CLUBS = 2;
public final static int HEARTS = 3;
public final static int SPADES = 4;

// Kinds of ranks
public final static int ACE = 1;
public final static int DEUCE = 2;
public final static int THREE = 3;
public final static int FOUR = 4;
public final static int FIVE = 5;
public final static int SIX = 6;
public final static int SEVEN = 7;
public final static int EIGHT = 8;
public final static int NINE = 9;
public final static int TEN = 10;
public final static int JACK = 11;
public final static int QUEEN = 12;
public final static int KING = 13;

public Card(int rank, int suit) {
assert isValidRank(rank);
assert isValidSuit(suit);
this.rank = rank;
this.suit = suit;
}

public int getSuit() {
return suit;
}

public int getRank() {
return rank;
}

public static boolean isValidRank(int rank) {
return ACE <= rank && rank <= KING;
}

public static boolean isValidSuit(int suit) {
return DIAMONDS <= suit && suit <= SPADES;
}

public static String rankToString(int rank) {
switch (rank) {
case ACE:
return "Ace";
case DEUCE:
return "Deuce";
case THREE:
return "Three";
case FOUR:
return "Four";
case FIVE:
return "Five";
case SIX:
return "Six";
case SEVEN:
return "Seven";
case EIGHT:
return "Eight";
case NINE:
return "Nine";
case TEN:
return "Ten";
case JACK:
return "Jack";
case QUEEN:
return "Queen";
case KING:
return "King";
default:
//Handle an illegal argument. There are generally two
//ways to handle invalid arguments, throwing an exception
//(see the section on Handling Exceptions) or return null
return null;
}
}

public static String suitToString(int suit) {
switch (suit) {
case DIAMONDS:
return "Diamonds";
case CLUBS:
return "Clubs";
case HEARTS:
return "Hearts";
case SPADES:
return "Spades";
default:
return null;
}
}

public static void main(String[] args) {

// must run program with -ea flag (java -ea ..) to
// use assert statements
assert rankToString(ACE) == "Ace";
assert rankToString(DEUCE) == "Deuce";
assert rankToString(THREE) == "Three";
assert rankToString(FOUR) == "Four";
assert rankToString(FIVE) == "Five";
assert rankToString(SIX) == "Six";
assert rankToString(SEVEN) == "Seven";
assert rankToString(EIGHT) == "Eight";
assert rankToString(NINE) == "Nine";
assert rankToString(TEN) == "Ten";
assert rankToString(JACK) == "Jack";
assert rankToString(QUEEN) == "Queen";
assert rankToString(KING) == "King";

assert suitToString(DIAMONDS) == "Diamonds";
assert suitToString(CLUBS) == "Clubs";
assert suitToString(HEARTS) == "Hearts";
assert suitToString(SPADES) == "Spades";

}
}

The above provides an example of a Card class, again taken from Oracle’s Java tutorials. Let’s walk through it.

After the name of the class and an initial curly brace, two fields are declared: rank and suit. After this follow all the kinds of suits, then the kinds of ranks. This is followed by a special kind of method, called a constructor, which may be used by other objects to generate an instance of the Card class, then a pair of methods getSuit() and getRank(), which respectively provide, or return, the value of the suit and rank of a card object to whatever object makes use of, or calls, these methods. This pair of functions belongs to a larger class of methods called getters, whose naming and functionality follow a longstanding coding convention. This is followed by two Boolean methods isValidRank() and isValidSuit(), the methods suitToString() and rankToString(), and the main() method. The Boolean methods, named after the logician George Boole, are so-called because the values they return are ‘true’ and ‘false’, which are represented by the numbers 1 and 0 in the two-valued Boolean number system all computers use. The ToString() methods convert an integer data type to a corresponding string of letters. For instance, when provided the number 2, the rankToString() method returns the string ‘Two’. Common best practice for real-world Java applications places the main method in its own, separate Main class, so we’ll leave that method aside for now.

In short, the Card class above consists of two fields, representing common properties of a card object, a makeshift identification of the card ranks and suits with integers that makes them easier to functionally manipulate, and a set of methods that perform various operations on the card’s fields. Related classes are bundled together into a package, which are to Classes what a file folder is to files.

3 OOP Objects as monads

Thus, object-oriented programming classes are integrated into a larger whole, and have methods that act reflexively on their states, where these methods may treat the things they act on either in accordance with the nature of what they represent or in accordance with their nature as objects in an program. Because of the first condition, classes themselves serve as abstract parts in an abstract whole. When a program using these classes is run, objects which are instances of these classes are created and their methods are used in accordance with user input and the instructions in the code itself. Because of this, objects can, in turn, exist in other objects.

Philosophy has a name for an object which may exist in another object, has behavior which is reflexive upon its own state, and responds to the behavior of other objects through its own spontaneous behavior, such that all these beings exist as part of a harmoniously working whole: a monad. The term goes back to Gottfried Leibniz, who introduced the concept to provide a unified solution to the problems involved in explaining both interactions between different bodies and the relation of the body to the soul. On Leibniz’s account, objects don’t genuinely change each other’s states. Instead, God so closely harmonized these various objects in their nature at the beginning of creation that a change in one object would automatically result in a corresponding, albeit spontaneous, change in others. Object-oriented objects act on their own states, exist as parts in a harmonious whole and are all ordered by one dominant method called the main method, just as Leibniz’s monads act reflexively on themselves in a harmonious world ordered perfectly by God from creation.

4 Conclusion

Though object-oriented programming objects are often presented as being strictly analogous to real-world objects, this analogy breaks down fairly quickly. While OOP fields are similar to the static properties of real-world objects, the dynamic behavior of OOP objects is fundamentally different from that of real-world objects: OOP methods don’t explicitly distinguish instances of classes as representations from those same objects as instances of code, and the methods bundled together in a class are bundled with that class (rather than another) because of the manner in which they reflect on, utilize, or otherwise manipulate the object’s more basic states. OOP classes are parts in a whole package, and the methods listed for a given class will depend on the role it plays in that whole (or a similar one). When a program is run, the exact order in which various objects act is given by a master method called the main method, usually set off in its own class. In these ways, objects which instantiate classes in packages are closer to the philosophical account of objects found in Leibniz’s account of monads as parts of a harmonious world which act reflexively on their own states in a manner God coordinates, than they are to objects as commonly understood.

Originally published at http://jacobarchambault.com on May 11, 2019.

📝 Read this story later in Journal.

👩‍💻 Wake up every Sunday morning to the week’s most noteworthy stories in Tech waiting in your inbox. Read the Noteworthy in Tech newsletter.

--

--