Java Tip 27: Typesafe constants in C++ and Java
2013-05-22 21:01
591 查看
This article offers an alternative to using
to help eliminate range checking and runtime errors -- first looking at C++ and then exploring an implementation of the same using Java. The summary above shows one of the problems associated with using these constructs. Another problem is the additional range
checking that is required to validate the parameter (see below).
Probably the best known and most widely used approach in C++ is through the use of enum.
The main problem with the approach above is that any
or someone who knows that
the responsibility of range-checking the parameter.
The next example takes advantage of the C++ compiler's type checking, but it is still open to abuse through the use of a cast.
Even though
stops clients of
it is still open to abuse by undermining the compiler's type checking through the use of a cast. For example, the compiler can not prevent someone from casting an
Now this leaves us back at square one. Again a cautious implementor of car_wash() will feel obliged to do some form of range checking.
NOTE: The
could also be replaced by
Below I offer an alternative to the above methods that gives both type safety and "constness."
Notice the use of the private ctor (constructor) this stops the instantiation of client
instantiate their own objects, the need to perform any range checking in
One last area of improvement would be to remove the nasty
problem with enumerated types or constants. Although client code becomes more readable through the use of constant objects, the class methods that receive these objects as parameters can become giant switch statements or a series of
In a future tip I will explore various techniques that can be used to eliminate these problems.
In the meantime I will leave it as an exercise for the reader to come up with an alternative design for
Finally, for those of you who have been patient enough to wade through C++ desperate to get your hands on some Java (and who can blame you!), here is the Java implementation of the
AWT (abstract windowing toolkit) uses a similar approach in some classes.
Notice that in this case, as in many others, the Java implementation is more elegant than the C++ implementation. Also note the use of "final" in the class declaration. The "final" keyword declares that a class can not be subclassed.
At this point some of you may be thinking that the declaration of class
to add new cars. Although not an award-winning design, the Java code below illustrates how the subclassing of class
The first thing to note in the code above is that the potential exists for clients of
To improve matters,
started -- trying to design typesafe constants!
It is left as an exercise for the reader to explore this last technique and balance the advantages with the tradeoffs.
phil@eveque.demon.co.uk.
enums or
const ints
to help eliminate range checking and runtime errors -- first looking at C++ and then exploring an implementation of the same using Java. The summary above shows one of the problems associated with using these constructs. Another problem is the additional range
checking that is required to validate the parameter (see below).
Probably the best known and most widely used approach in C++ is through the use of enum.
class Car { public: enum{FORD,VW,SAAB,MAX_CAR}; }; void car_wash(int car) { //requires range checking on Cars if(car>=0 && car<MAX_CAR) { //ok } } void main(void) { car_wash(Car::FORD); }
The main problem with the approach above is that any
intvalue or object with a conversion to
int(that is, a class with
operator int()) can be passed to
car_wash(). For example, there is nothing stopping someone from using
car_wash()like this:
car_wash(100);--
or someone who knows that
Car::FORDis zero from being lazy and calling
car_wash(0). This leaves the implementor of
car_wash()with
the responsibility of range-checking the parameter.
The next example takes advantage of the C++ compiler's type checking, but it is still open to abuse through the use of a cast.
class Car { public: enum Type{FORD,VW,SAAB}; }; void car_wash(Car::Type c) { //do something with c } void main(void) { Car::Type car=Car::FORD; car_wash(car); //OK car_wash is expecting a Car::Type parameter car_wash(1); // error, no conversion from int Car::Type }
Even though
Car::Typeis an
enumand therefore ultimately is an
int, the compiler
stops clients of
car_wash()from passing an
intas a parameter. However, although this approach is an improvement on the previous example,
it is still open to abuse by undermining the compiler's type checking through the use of a cast. For example, the compiler can not prevent someone from casting an
intto a Car::Type.
void main(void) { Car::Type car=(Car::Type)100; car_wash(car); }
Now this leaves us back at square one. Again a cautious implementor of car_wash() will feel obliged to do some form of range checking.
NOTE: The
classkeyword can be replaced by
namespacein these C++ examples if your compiler supports it and the
enums
could also be replaced by
const int. But the problem we are trying to eliminate remains the same: the need for range checking and the potential for runtime errors.
Below I offer an alternative to the above methods that gives both type safety and "constness."
class Car { private: //private ctor to stop instantiation of client Car objects Car() {} public: static const Car FORD; static const Car VW; static const Car SAAB; int operator==(const Car &car) const { return &car==this; } }; //static initialization const Car Car::FORD; const Car Car::VW; const Car Car::SAAB; void car_wash(const Car &car) { if(Car::FORD==car) { cout << "The Car being washed is a FORD" << endl; } else if(Car::VW==car) { cout << " The Car being washed is a VW" << endl; } // } void main(void) { car_wash(Car::VW); }
Notice the use of the private ctor (constructor) this stops the instantiation of client
Carobjects. Because clients of class
Carcan not
instantiate their own objects, the need to perform any range checking in
car_wash()is eliminated.
One last area of improvement would be to remove the nasty
if, else ifclauses from
car_wash(). The presence of these clauses is a common
problem with enumerated types or constants. Although client code becomes more readable through the use of constant objects, the class methods that receive these objects as parameters can become giant switch statements or a series of
if, else ifclauses.
In a future tip I will explore various techniques that can be used to eliminate these problems.
In the meantime I will leave it as an exercise for the reader to come up with an alternative design for
car_wash()that removes the
if, else ifclauses.
Finally, for those of you who have been patient enough to wade through C++ desperate to get your hands on some Java (and who can blame you!), here is the Java implementation of the
Carclass. The
AWT (abstract windowing toolkit) uses a similar approach in some classes.
public final class Car { private Car() {} public static final Car FORD=new Car(); public static final Car VW=new Car(); public static final Car SAAB=new Car(); }
Notice that in this case, as in many others, the Java implementation is more elegant than the C++ implementation. Also note the use of "final" in the class declaration. The "final" keyword declares that a class can not be subclassed.
At this point some of you may be thinking that the declaration of class
Caras final may be too restrictive and that the class should be non-final with a protected ctor to allow subclasses
to add new cars. Although not an award-winning design, the Java code below illustrates how the subclassing of class
Carcan reintroduce the problems we have been trying to eliminate.
import java.util.Vector; public class Car { protected Car() {} public static final Car FORD=new Car(); public static final Car VW=new Car(); public static final Car SAAB=new Car(); } public class FrenchCar extends Car { public static final Car CITROEN=new Car(); protected FrenchCar() { } } public class CarWash { public void Start(Car car) { // } } public class SuperCarWash extends CarWash { public void Start(Car car) { if(FrenchCar.CITROEN==car) { //do some specialized washing //could still call super.Start(car) //to finish off } else super.Start(car); } } public class SuperCarWashOwner { private SuperCarWash superCarWash= new SuperCarWash(); private Vector carsToWash = new Vector(); public void AddCar(Car c) { carsToWash.addElement(c); } public void WashCars() { //iterate cars an call superCarWash.Start } } public class Test { private SuperCarWashOwner scwOwner = new SuperCarWashOwner(); public void TestMethod() { scwOwner.AddCar(Car.VW); //add more cars scwOwner.WashCars(); } }
The first thing to note in the code above is that the potential exists for clients of
CarWashto pass a
FrenchCarto
CarWash.Start().
To improve matters,
CarWash.Start()could throw an exception if a
FrenchCarobject is passed in, but all this leads us back to where we
started -- trying to design typesafe constants!
It is left as an exercise for the reader to explore this last technique and balance the advantages with the tradeoffs.
About the author
Philip Bishop is technical director and a consultant at Eveque Systems Ltd. in the UK. Eveque specializes in designing and implementing distributed object systems using Java, CORBA, C++, ODBMS, and so on. He can be reached atphil@eveque.demon.co.uk.
相关文章推荐
- 译 Programming with typesafe enums and annotations in Java 5
- Virtual method and base-type pointer make polymorphism in C++
- Name for argument type [java.lang.String] not available, and parameter name information not found in
- Fail Fast and Fail Safe Iterators in Java
- Polymorphism in Perl comparing with JAVA and C++
- Pass-by-reference in C++ and java
- Retrofit A type-safe HTTP client for Android and Java
- Java is safe and secure in Oracle's hands
- common and different of final in java and const in C++
- static in Java and C++
- public、protected、private in c++ and java
- TypeSafe config & HOCON + Read properties in java (with example)
- Retrofit——A type-safe HTTP client for Android and Java(The first part)
- Type difference of character literals in C and C++
- Retrofit——A type-safe HTTP client for Android and Java(The first part)
- Typesafe DSLs in Java: Part 1 — Typesafe Bytecode
- type dedution and reference collapse in c++ 11(works vc++ 2013)
- @RequestParam注解使用:Name for argument type [java.lang.String] not available, and parameter name information not found in class file either.
- Type Annotations in Java 8: Tools and Opportunities
- Name for argument type [java.lang.String] not available, and parameter name information not found in