java final vs. c++ const

N

Nan Li

Hello,
In C++, when you say const A* p, it means the object that p
points to is a constant, while A* const p means p is a constant. I
have noticed in Java, 'final A p' is equivalent to the second form in
C++, where p is not reassignable. Does any one know if there is a way
to achieve the first form in java ? Thanks a lot.


Sample programs:

//ConstantTest.java
class A
{
public int i;
}


public class ConstantTest {


public static void main( String[] args ) {
final A a = new A();
a.i = 5;
a = new A(); //not OK
}
}
--------------------------------
//ConstantTest.cpp
//there is mem leak in the code, demo only.

class A
{
public:
int i;
};


int main() {
A* const a = new A();
const A* ya = new A();
a->i = 5;
a = new A(); //not OK
ya->i = 5; //not OK
ya = new A();
return 0;
}
 
O

Oliver Wong

Nan Li said:
Hello,
In C++, when you say const A* p, it means the object that p
points to is a constant, while A* const p means p is a constant. I
have noticed in Java, 'final A p' is equivalent to the second form in
C++, where p is not reassignable. Does any one know if there is a way
to achieve the first form in java ? Thanks a lot.


Sample programs:

//ConstantTest.java
class A
{
public int i;
}


public class ConstantTest {


public static void main( String[] args ) {
final A a = new A();
a.i = 5;
a = new A(); //not OK
}
}
--------------------------------
//ConstantTest.cpp
//there is mem leak in the code, demo only.

class A
{
public:
int i;
};


int main() {
A* const a = new A();
const A* ya = new A();
a->i = 5;
a = new A(); //not OK
ya->i = 5; //not OK
ya = new A();
return 0;
}

Don't know much about C++, but from your example, it seems like "const
A* ya" is syntactic sugar for marking all of the member fields as final. You
can achieve that in Java like this:

<wontCompile>
class A {
final public int i;
}
</wontCompile>

But if you do that, you need to make sure that i gets initialized somehow;
e.g., via a constructor:

<betterCode>
class A {
final public int i;

public A(int initialValueForI) {
this.i = initialValueForI;
}
}
</betterCode>

- Oliver
 
N

Nan Li

Oliver said:
Nan Li said:
Hello,
In C++, when you say const A* p, it means the object that p
points to is a constant, while A* const p means p is a constant. I
have noticed in Java, 'final A p' is equivalent to the second form in
C++, where p is not reassignable. Does any one know if there is a way
to achieve the first form in java ? Thanks a lot.


Sample programs:

//ConstantTest.java
class A
{
public int i;
}


public class ConstantTest {


public static void main( String[] args ) {
final A a = new A();
a.i = 5;
a = new A(); //not OK
}
}
--------------------------------
//ConstantTest.cpp
//there is mem leak in the code, demo only.

class A
{
public:
int i;
};


int main() {
A* const a = new A();
const A* ya = new A();
a->i = 5;
a = new A(); //not OK
ya->i = 5; //not OK
ya = new A();
return 0;
}

Don't know much about C++, but from your example, it seems like "const
A* ya" is syntactic sugar for marking all of the member fields as final. You
can achieve that in Java like this:

<wontCompile>
class A {
final public int i;
}
</wontCompile>

But if you do that, you need to make sure that i gets initialized somehow;
e.g., via a constructor:

<betterCode>
class A {
final public int i;

public A(int initialValueForI) {
this.i = initialValueForI;
}
}
</betterCode>

- Oliver

Making data members final is not quite the same as declaring object
constant in C++.
With the first approach, class designer forces the constness, while the
second one the users of the class have freedom to decide whether it
should be const or not.
 
L

Leroy42

You cannot do this in Java :-(

Starting programming in Java, I first missed
this construct, but you can live without it.

Hovewer if you want to assure that the
compiler forbids you when ANY method
changes the contents of an instance
when the method isn't allowed you can
use a "dirty workaround".

1. Declare all members of the class
private or protected.
2. Define an Interface with similar name
that ONLY defines those methods that
don't changes the "contents" of an
instance.
3. Tell your original class to implement
this interface.
4. Use the interface instead of the original
class for local vars or parameters if
you want to have them "const"

Example:

public class Const {
public static void main(String[] args) {
Klasse k = new Klasse();
normal(k);
konst(k);
}

static void normal(Klasse k) {
k.set(-42);
k.negate();
System.out.println(k.get());
}

static void konst(KlasseC kC) {
kC.set(-42); // Compile-Error
kC.negate(); // Compile-Error
System.out.println(kC.get()); // OK
}
}

interface KlasseC {
public int get();
}
class Klasse implements KlasseC {
protected int i;

public void set(int i) {this.i = i;}
public int get() {return i;}
public void negate() {i = -i;}
}

But this is only a "dirty" solution. If one
of the "get"-methods of your methods
return an object itself, this wan't work
in "deep"

Leroy42
 
C

Chris Smith

Nan Li said:
In C++, when you say const A* p, it means the object that p
points to is a constant, while A* const p means p is a constant. I
have noticed in Java, 'final A p' is equivalent to the second form in
C++, where p is not reassignable. Does any one know if there is a way
to achieve the first form in java ?

No.

There are, however, various patterns and idioms for handling the
requirement. These include:

1. The simplest way to make on object "constant" is to only give out
copies of the object. That way, the other side can modify their private
copy all they like, without having any impact on program correctness.
There are obvious performance costs.

2. The other option is to use a single object, but arrange for different
interfaces to that object based on const-ness. This requires that you
separate the exposed API of the object into interfaces in the first
place. There are three variations:

* Variation 2a: "False Unmodifiability"

Here, you define two interfaces and a class.

public interface UnmodifiableValue
{
public String getName();
}

public interface Value extends UnmodifiableValue
{
public void setName(String name);
}

public class ValueImpl implements Value { ... }

You can then pass around references of type Value or UnmodifiableValue.
It's "false" because a simple cast would allow someone to treat an
unmodifiable value as modifiable. Of course, the same is true of
const-ness in C++, where const_cast<T> can be used to accomplish this
goal. This is, in fact, the closest thing Java has to C's const when
applied to objects.

* Variation 2b: "Optional Operations"

Here, you write an adapter that just throws exceptions from some
operations. This is poor OO design, but nevertheless is sometimes used
when simplicity is valued above correctness of the exposed interface.

public interface Value
{
public String getName();
public void setName(String name);
}

public class ValueImpl implements Value { ... }

// and a method somewhere
static Value unmodifiableValue(final Value base)
{
return new Value() {
public void setName(String name)
{
throw new UnsupportedOperationException();
}

public String getName()
{
return base.getName();
}

};
}

This is used in the Collections API, for example.

Variation 2c. "Interface Adapter"

This is a combination of the previous two. 2a provide compile-time
const-ness. 2b provides runtime const-ness. This provides both.

public interface UnmodifiableValue
{
public String getName();
}

public interface Value extends UnmodifiableValue
{
public void setName(String name);
}

public class ValueImpl implements Value { ... }

// and a method somewhere
static UnmodifiableValue unmodifiableValue(final Value base)
{
return new Value() {
public String getName()
{
return base.getName();
}

};
}

Hopefully, one of the above will meet your needs.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
O

Oliver Wong

Chris Smith said:
Variation 2c. "Interface Adapter"

This is a combination of the previous two. 2a provide compile-time
const-ness. 2b provides runtime const-ness. This provides both.

public interface UnmodifiableValue
{
public String getName();
}

public interface Value extends UnmodifiableValue
{
public void setName(String name);
}

public class ValueImpl implements Value { ... }

// and a method somewhere
static UnmodifiableValue unmodifiableValue(final Value base)
{
return new Value() {
public String getName()
{
return base.getName();
}

};
}

I personally don't like the idea that a modifiable value extends an
unmodifiable value. Suppose the client code receives something which they
assume is unmodifiable and uses it as the key of a HashMap, for example?

I prefer a 3-class design, like the following:

<code>
abstract class Value {
public String getName();
}

class UnmodifiableValue extends Value {

final private String name;

public UnmodifiableValue(String name) {
this.name = name;
}

public UnmodifiableValue(Value v) {
this.name = v.getName();
}

@Override
public String getName() {
return this.name;
}
}

class ModifiableValue extends Value {
private String name;

public ModifiableValue(Value v) {
this.name = v.getName();
}

@Override
public String getName() {
return this.name;
}

public void setName(String name) {
this.name = name;
}
}
</code>

Note the "copy-constructors" that allow you to create an equivalent
ModifiableValue given an UnmodifiableValue and vice versa.

- Oliver
 
R

Roedy Green

In C++, when you say const A* p, it means the object that p
points to is a constant, while A* const p means p is a constant. I
have noticed in Java, 'final A p' is equivalent to the second form in
C++, where p is not reassignable. Does any one know if there is a way
to achieve the first form in java ? Thanks a lot.

the closest to that is as immutable object. The next best thing is an
interface-style reference to an object with no mutating methods.

There are several others tricks.

see http://mindprod.com/jgloss/immutable.html
 
A

AndyRB

Chris said:
You can then pass around references of type Value or UnmodifiableValue.
It's "false" because a simple cast would allow someone to treat an
unmodifiable value as modifiable. Of course, the same is true of
const-ness in C++, where const_cast<T> can be used to accomplish this
goal.

If the goal is simply "to treat an unmodifiable value as modifiable"
then yes you are right. However, if the goal is for the program to
retain well-defined semantics and not produce some random output as
well as to treat an unmodifiable value as modifiable, const_cast will
only accomplish that particular goal in a couple of specific
circumstances.

C++ often allows you to lie to the compiler and at that point...you're
on your own!
 
C

Chris Smith

AndyRB said:
If the goal is simply "to treat an unmodifiable value as modifiable"
then yes you are right. However, if the goal is for the program to
retain well-defined semantics and not produce some random output as
well as to treat an unmodifiable value as modifiable, const_cast will
only accomplish that particular goal in a couple of specific
circumstances.

Care to explain? In what contexts (aside from String literals and
static or global const values) would const_cast<T> produce undefined
results when used to cast away const-ness of the target of a pointer?

I'm really asking... I don't know the C++ spec well enough to answer,
and it's unfortunately no publicly available.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
A

AndyRB

Chris said:
Care to explain? In what contexts (aside from String literals and
static or global const values) would const_cast<T> produce undefined
results when used to cast away const-ness of the target of a pointer?

Well technically it is not the const_cast itself which invokes
undefined beahviour, but any subsequent attempt to modify *any* const
object through the pointer.

void foo(const int * cp)
{
int * p = const_cast<int*>(cp); // cast required

*p = 4; // undefined if the underlying object is a const object
}

int main()
{
const int ci = 3;
int i = 2;
foo(&i);//cast and assignment in foo OK,
//underlying object is not const.
foo(&ci);//cast OK subsequent assignment in foo not OK,
//underlying object is still const.
}

The code in foo is therefore unsafe, as you have no way of ensuring
that the int which cp points to is really non-const.
 
C

Chris Smith

AndyRB said:
Well technically it is not the const_cast itself which invokes
undefined beahviour, but any subsequent attempt to modify *any* const
object through the pointer.

Ah, now I understand. Does this also apply to const instance fields of
non-const objects?

In any case, the point is the same regarding my original post... except
that the C++ code is slightly less statically safe in that its behavior
is completely undefined, while a similar Java application may still
obtain defined behavior but would be unable to exhibit *sensible*
behavior since the assumptions of the remaining application are broken.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
A

AndyRB

Chris said:
Ah, now I understand. Does this also apply to const instance fields of
non-const objects?

Yes, if the data member is defined as const then it cannot be safely
modified.
In any case, the point is the same regarding my original post... except
that the C++ code is slightly less statically safe in that its behavior
is completely undefined, while a similar Java application may still
obtain defined behavior but would be unable to exhibit *sensible*
behavior since the assumptions of the remaining application are broken.

I would largely agree with that.
In c++ it is an example of by-passing the type system, something that
you can't do in Java, but is necessary in c++ in order to be able to
write certain low-level code.
 
L

Luc The Perverse

AndyRB said:
I would largely agree with that.
In c++ it is an example of by-passing the type system, something that
you can't do in Java, but is necessary in c++ in order to be able to
write certain low-level code.

Being able to receive a copy of an object without any additional help is
probably a huge inefficiency in C++ - since the overhead of creating a copy
is high.

But I do miss it a little.

I would say though - it's not a reason not to use Java.

I've never considered it "necessary" though. You can make a copy of an
object in Java if you want to, you just have to do it explicitly by creating
a new object (same as C++ does transparently). What low level code are you
refering to?
 
A

AndyRB

My post was refering to casting not copying but...
Being able to receive a copy of an object without any additional help is
probably a huge inefficiency in C++ - since the overhead of creating a copy
is high.
It is generally very inefficient if the object is large and
particularily if it contains a dynamically allocated buffer. For other
User defined types it is generally less efficient, how much largely
depends on the compiler, a good optimiser may be able to optimise out
many copies.
But I do miss it a little.
not being able to do the equivalent of - foo(const &object) - please
ignore the reference (I'm not refering to pass-by-reference here) seems
far more important to Java...
I would say though - it's not a reason not to use Java.
I don't think this kind of copying (using value semantics) is even
relevant to Java objects, as they are always polymorphic.
I've never considered it "necessary" though. You can make a copy of an
object in Java if you want to, you just have to do it explicitly by creating
a new object (same as C++ does transparently).

If you are dealing with polymorphic objects in c++, you would use a
similar cloning technique so that you get reference sematics. Value
semantics and polymorphic objects generally don't mix, remember in c++
the default object is a non-polymorphic (no inheritance, member
functions non-virtual by default) and it is for these objects that the
kind of copying in c++ you are refering to makes sense.
What low level code are you
refering to?
I was refering to using casting to bypass the type system (specifically
with reinterpret_cast in mind), i.e. dealing with raw memory that has
to be reinterpreted as different data types.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,186
Members
46,740
Latest member
JudsonFrie

Latest Threads

Top