Why Are Private Fields Private to the Type, Not the Instance

Why are private fields private to the type, not the instance?

I think one reason it works this way is because access modifiers work at compile time. As such, determining whether or not a given object is also the current object isn't easy to do. For example, consider this code:

public class Foo
{
private int bar;

public void Baz(Foo other)
{
other.bar = 2;
}

public void Boo()
{
Baz(this);
}
}

Can the compiler necessarily figure out that other is actually this? Not in all cases. One could argue that this just shouldn't compile then, but that means we have a code path where a private instance member of the correct instance isn't accessible, which I think is even worse.

Only requiring type-level rather than object-level visibility ensures that the problem is tractable, as well as making a situation that seems like it should work actually work.

EDIT: Danilel Hilgarth's point that this reasoning is backwards does have merit. Language designers can create the language they want, and compiler writers must conform to it. That being said, language designers do have some incentive to make it easier for compiler writers to do their job. (Though in this case, it's easy enough to argue that private members could then only be accessed via this (either implicitly or explicitly)).

However, I believe that makes the issue more confusing than it needs to be. Most users (myself included) would find it unneccessarily limiting if the above code didn't work: after all, that's my data I'm trying to access! Why should I have to go through this?

In short, I think I may have overstated the case for it being "difficult" for the compiler. What I really meant to get across is that above situation seems like one that the designers would like to have work.

Why can an instance of a class access private fields of another instance of its own type?

First you have to ask "why have private fields at all?"

Private fields are primarily for encapsulation: a consumer of a class shouldn't have to know the internals of that class' implementation, and in fact those internals should be actively hidden from the consumer. Otherwise, if a user relied on those internals, then the implementer would be forced to support them or break backwards compatibility. In other words, it protects both the user and designer of the class:

  • users are protected from implementation changes breaking their code
  • the designer is protected from having to keep implementation details features unchanged forever

But a class doesn't need to be protected from itself; it doesn't need to worry about the case where one bit of its code changes, but another bit (that uses the first bit) can't change. Backwards compatibility is not a concern, because the class is developed and deployed as a single, atomic chunk of code. In other words, neither of the above protections are needed.

Since there's no need to protect the fields, and since it's often necessary to see them (for instance, to compare if two objects are equal), they're visible within the class.

In Java, why are the private data members accessible outside the class if written like this?

Because the method is in the same class, it can access private members of other instances. private only means that only functions in the class can access it, with no restrictions on which object is doing the accessing.

What are the differences between the private keyword and private fields in TypeScript?

Private keyword

The private keyword in TypeScript is a compile time annotation. It tells the compiler that a property should only be accessible inside that class:

class PrivateKeywordClass {
private value = 1;
}

const obj = new PrivateKeywordClass();
obj.value // compiler error: Property 'value' is private and only accessible within class 'PrivateKeywordClass'.

However compile time checking can be easily bypassed, for example by casting away the type information:

const obj = new PrivateKeywordClass();
(obj as any).value // no compile error

The private keyword is also not enforced at runtime

Emitted JavaScript

When compiling TypeScript to JavaScript, the private keyword is simply removed:

class PrivateKeywordClass {
private value = 1;
}

Becomes:

class PrivateKeywordClass {
constructor() {
this.value = 1;
}
}

From this, you can see why the private keyword does not offer any runtime protection: in the generated JavaScript it's just a normal JavaScript property.

Private fields

Private fields ensure that properties are kept private at runtime:

class PrivateFieldClass {
#value = 1;

getValue() { return this.#value; }
}

const obj = new PrivateFieldClass();

// You can't access '#value' outside of class like this
obj.value === undefined // This is not the field you are looking for.
obj.getValue() === 1 // But the class itself can access the private field!

// Meanwhile, using a private field outside a class is a runtime syntax error:
obj.#value

// While trying to access the private fields of another class is
// a runtime type error:
class Other {
#value;

getValue(obj) {
return obj.#value // TypeError: Read of private field #value from an object which did not contain the field
}
}

new Other().getValue(new PrivateKeywordClass());

TypeScript will also output a compile time error if you try using a private field outside of a class:

Error on accessing a private field

Private fields originates from a TC-39 ECMAScript proposal and are part of the 2021 ECMAScript specification, which means that they can be used in both normal JavaScript and TypeScript.

Emitted JavaScript

If you use private fields in TypeScript and are targeting older versions of JavaScript for your output, such as es6 or es2018, TypeScript will try to generate code that emulates the runtime behavior of private fields

class PrivateFieldClass {
constructor() {
_x.set(this, 1);
}
}
_x = new WeakMap();

If you are targeting esnext, TypeScript will emit the private field:

class PrivateFieldClass {
constructor() {
this.#x = 1;
}
#x;
}

Which one should I use?

It depends on what you are trying to achieve.

The private keyword is a fine default. It accomplishes what it was designed to accomplish and has been used successfully by TypeScript developers for years. And if you have an existing codebase, you do not need to switch all of your code to use private fields. This is especially true if you are not targeting esnext, as the JS that TS emits for private fields may have a performance impact. Also keep in mind that private fields have other subtle but important differences from the private keyword

However if you need to enforce runtime privateness or are outputting esnext JavaScript, than you should use private fields.

Also keep in mind that organization/community conventions on using one or the other will also evolve as private fields become more widespread within the JavaScript/TypeScript ecosystems

Other differences of note

  • Private fields are not returned by Object.getOwnPropertyNames and similar methods

  • Private fields are not serialized by JSON.stringify

  • There are importance edge cases around inheritance.

    TypeScript for example forbids declaring a private property in a subclass with the same name as a private property in the superclass.

    class Base {
    private value = 1;
    }

    class Sub extends Base {
    private value = 2; // Compile error:
    }

    This is not true with private fields:

    class Base {
    #value = 1;
    }

    class Sub extends Base {
    #value = 2; // Not an error
    }
  • A private keyword private property without an initializer will not generate a property declaration in the emitted JavaScript:

    class PrivateKeywordClass {
    private value?: string;
    getValue() { return this.value; }
    }

    Compiles to:

    class PrivateKeywordClass {
    getValue() { return this.value; }
    }

    Whereas private fields always generate a property declaration:

    class PrivateKeywordClass {
    #value?: string;
    getValue() { return this.#value; }
    }

    Compiles to (when targetting esnext):

    class PrivateKeywordClass {
    #value;
    getValue() { return this.#value; }
    }

Further reading:

  • The future of the "private" keyword
  • TypeScript PR that added private fields

Why I can not access private field of a base class when private variables can be accessed this way

If we can access the private variables of Class as mentioned then why a class which extends another class can not access private variables of base class?

Because the variable isn't declared in the program text of the derived class, which is how private access is defined for Java and C#. (The details around nested classes vary between Java and C#; if you want details for one language or the other, please be more specific in your question.)

I believe when code or classes will be compiled then all the methods, variables etc of base class will be added to derived class. Am I right on this?

They're not added to the class, no. They're inherited by the derived class, and any instance of the derived class will have all the fields that are inherited - but that's not the same as them behaving as if they were declared in the derived class.

Why doesn't YamlDotNet serialize private fields of the class?

The reason why YamlDotNet serializes only public properties by default is because doing otherwise would break encapsulation. Accessing private members would mean that the model would be unable to guarantee its invariants. If you compare with other libraries, such as Json.NET, you will notice that they use the same approach.

I don't think that this is a problem because you should not be (de)serializing your domain model directly. Doing so would constrain your domain model to your serialization schema, which in many cases will need to be different. That's the same problem as trying to map your domain model to a relational database.

Instead, you should define a serialization model and map between your domain model and that serialization model. In that case there's no need to serialize private fields.

That said, if you really want to serialize private fields, that's trivial to do. You need
register you own implementation of ITypeInspector that returns private fields. You can base your implementation on ReadableFieldsTypeInspector.

Access private field of a instance object

Privacy is not per instance - it's per class.

The class can access the private fields of all instances.

For example, the method equals( Object o ) can cast o (if appropriate) to the same type, and compare its private members with the object on which equals() was called.



Related Topics



Leave a reply



Submit