46

How can we have a variable that is writable within the class but only "readable" outside it?

For example, instead of having to do this:

Class C {
  private int width, height;

  int GetWidth(){
    return width;
  }

  int GetHeight(){
    return height;
  }

  // etc..

I would like to do something like this:

Class C {
  public_readonly int width, height;

  // etc...

What's the best solution?

Pacerier
  • 86,231
  • 106
  • 366
  • 634

7 Answers7

50

Create a class with public final fields. Provide constructor where those fields would be initialized. This way your class will be immutable, but you won't have an overhead on accessing the values from the outside. For example:

public class ShortCalendar
{
    public final int year, month, day;

    public ShortCalendar(Calendar calendar)
    {
        if (null == calendar)
            throw new IllegalArgumentException();

        year = calendar.get(Calendar.YEAR);
        month = calendar.get(Calendar.MONTH);
        day = calendar.get(Calendar.DATE);
    }
}
Aleks N.
  • 6,051
  • 3
  • 42
  • 42
  • 2
    Getters have nearly no overhead in Java. The language is designed for this style of programming and is meant to deal with a huge amount of getters. Simple getters (which most getters are) will be optimized by the JVM giving the access speed of nearly native code. – Thomas Uhrig Feb 20 '14 at 15:37
  • 14
    First of all, I don't oppose using getters where it's really necessary. Often though they just bloat your code with boilerplate oneliners. Java is a clumsy langage, luckily there is a narrow set of cases where you can get something as elegant as Objective C properties for example. – Aleks N. Feb 20 '14 at 15:51
  • 3
    @AlaksiejN., This solution doesn't work if you need to modify the values within the class after object construction...... – Pacerier Feb 20 '14 at 19:23
  • 2
    @Pacerier Yep, this is the expected behavior. If you need a class that is (much) more complex than a C struct go with with other guys' proposals. – Aleks N. Feb 21 '14 at 07:59
25

There's no way to do this in Java.

Your two options (one which you mentioned) are using public getters and making the field private, or thorough documentation in the class.

The overhead on getter methods in extremely small (if at all). If you're doing it a large number of times, you might want to cache the fetched value instead of calling the get method.

EDIT:

One way to do it, although it has even more overhead than the getter, is defining a public inner class (let's call it innerC) with a constructor that is only available to your C class, and make your fields public. That way, you can't create innerC instances outside your class so changing your fields from outside is impossible, yet you can change them from inside. You could however read them from the outside.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • 2
    Is the edit complete? Should the fields in the inner class be protected against changing from outside by specifying them as final, thus leaving the outer class the only 1 that can change the value by reinstantiating the inner class? Even if I consider that, I'm not sure there's any way to prevent users of Class C from storing a reference to innerC, and then resetting innerC to the old reference after C reinstantiate innerC to set a new value. Maybe some code snippet illustrating your 'edit' can help clear my confusion? – yjw Nov 16 '11 at 17:32
  • You're right, storing old values would render this solution obsolete. Didn't think about that. – Luchian Grigore Nov 16 '11 at 21:09
  • 1
    the innerC field could still be set to null from outside – jamp May 24 '14 at 15:42
  • That's right, I think you can add this is readonly outside of class etc. to let other know – Cflowe Visit Apr 05 '22 at 14:58
6

from what I know the compiler does not optimize this kind of code

The Hotspot JIT compiler most definitely does.

I was wondering what's the best solution?

Stop optimizing prematurely. If you're using this code in a painting routine, I can guarantee that the actual painting will take at least a hundred times longer than calling a trivial method, even if it's not inlined.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
  • Heys btw I'm not understanding your first sentence, do you mean that the JIT compiler inlines the call? (and I was wondering if there is a document talking about that behavior (for verification)) – Pacerier Nov 16 '11 at 14:56
  • @Pacerier: That's what I mean, and I've added a link for confirmation. Note that it's part of an article from 1999 - I believe this capability was already in the first version of Hotspot, which came with Java 1.3 – Michael Borgwardt Nov 16 '11 at 15:01
  • @MichaelBorgwardt Heys I was thinking about this question again, I was wondering when they were talking about "avoiding getfield opcode" (http://stackoverflow.com/questions/4761681/avoiding-getfield-opcode), are they trying to reduce the overhead of this stuff? – Pacerier Nov 19 '11 at 21:56
  • @Pacerier: In that case the code even tries to avoid accessing a field repeatedly by copying it into a local variable. Note that if that actually has an effect, it can be done just as well with a get method than with a directly accessible field. – Michael Borgwardt Nov 19 '11 at 23:14
  • @MichaelBorgwardt hmm yes I was wondering why is it that it actually does *have* an effect, because I was thinking the runtime should have already optimize these kind of things (i.e. automatically copying the field locally), shouldn't it ? – Pacerier Nov 20 '11 at 06:54
  • @Pacerier: I agree, as does everyone in that question. But the trim() code is probably very old, from a time when JIT compilers were much less sophisticated. – Michael Borgwardt Nov 20 '11 at 10:43
4

there is no way to make a field "read only" from outside. the only - and right - way is to make the fields private and provide only getters, no setters.

Thomas Uhrig
  • 30,811
  • 12
  • 60
  • 80
  • 1
    Yes and no. Sure it is "read only" because it is final ;) But it is final from "inside" and "outside" - I mean, it is final, what else can I say? The answer to the original question (Did you read it?) is no. Of course you can set a final variable once, but the original question asked for some "read-only-from-outside" keyword which simply doesn't exist. And it's not final ;) – Thomas Uhrig Feb 20 '14 at 15:35
  • The author asks about a field that is settable only within the scope of a class itself. Thus my solution satisfies this clause. We don't know from the description of the question whether it's necessary to constantly modify the field. – Aleks N. Feb 20 '14 at 15:44
  • 2
    He _explicitly_ asks for a solution to make it read-only from _outside_. Only from outside. That's what the whole question is about. – Thomas Uhrig Feb 20 '14 at 15:46
  • The class example shown by the author is a good case for an immutable object. Thus `public final` is a good option to consider. – Aleks N. Feb 20 '14 at 15:53
  • You don't know. You don't know what "C" might be and whether its height and width could change. – Thomas Uhrig Feb 20 '14 at 15:57
  • 1
    Well, you know I'm not fond of harping on a subject. My intetion was to give options here instead of saying no way it is possible. – Aleks N. Feb 20 '14 at 16:14
  • Removing getter does not prevent those fields to be accessed via reflection. If you set that fields property, "setAccessible" to true then you can still access private field with no getter. – Pran Kumar Sarkar Feb 13 '22 at 06:04
1

Actually the HotSpot compiler would most likely inline calls to your getters, so no overhead will be involved (besides, the overhead for calling these methods would hardly be measurable).

EDIT

If you really need every CPU cycle, use C or C++ (or write performance-critical parts in it and call it via JNA, though its unlikely that it will be worth the time spent).

helpermethod
  • 59,493
  • 71
  • 188
  • 276
0

Your solution is: private fields, private setters, protected or public getters (note: protected allows access from same package as well as from subclasses)

RokL
  • 2,663
  • 3
  • 22
  • 26
  • 2
    @jamp apparently you've neither read up on the subject of access modifiers in java (such as: http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html) nor have you tried an example of this to see if it would compile. Please refrain from commenting on other people's answers if you're a beginner in Java. – RokL May 26 '14 at 07:32
  • @jamp, you're confusing _protected_ with _package_-protected/private (package-protected or package private) – Solomon Ucko Sep 23 '16 at 23:11
  • 2
    @jamp why don't remove your comment so people can get a clear answer. – user1935724 Jan 26 '17 at 00:56
-3

yes.. we can make a variable read-only using final keyword

Ex: public final int id = 10;

malith vitha
  • 473
  • 4
  • 4