Introduction
In programming, an enumeration is a data type that consists of a set of named values. In many programming languages, including C++, Java, and Kotlin, enumerations are implemented as a special type of class or structure, with each named value represented as a constant object of the enumeration type.
First, I discuss the basic concepts of enumeration in C++, then its implementation. Then the implementation of Java, Kotlin are discussed. Consequently, how the original concepts of enumeration in C++ expanded to other languages are described.
Concepts
Enumerations in all programming languages have the following concepts:
- enumerator: the items in the enumeration
- value representation: "Each enumerator is associated with a value of the underlying type." The values could be by default (increment of 1, starting from zero) or assigned.
- underlying type: " is an implementation-defined integral type that can represent all enumerator values"
- all enumerators in the enumeration must have the same type.
- size: the data type size of the enumerator.
- non-scoped enumeration: the enumeration values are part of the same scope as the enumeration type itself
- Values of C++ unscoped enumeration type are implicitly-convertible to integral types.
- The underlying type could be any of
int,unsigned int,long,unsigned long,long long, or unsigned long long.
- scoped enumeration: the enumeration values are scoped within the same enum class.
- Name of enumerator: Each enumerator becomes a named constant of the enumeration's type, which is contained within the scope of the enumeration, and can be accessed using scope resolution operator. Thus scope enumeration is also type-safe.
- Values of scoped enumeration is not implicitly convertible to integral types, although C++ static_cast may be used to obtain the numeric value of the enumerator.
Notes: Kotlin only has scoped enumeration, which is enum class and sealed class.
Comparing enumeration in C++ and other languages
| Concepts | C++ | Kotlint |
C++ implementation
Unscoped enumeration
- enum is declared with
enummodifier. The value of the enumerator could be intialized or used default. The value should not be larger than INT_MAX. - If the values of the enumerators are not initialized, the values are then assigned to indexed value in same way as array index, starting from zero.
The following illustrates 2 enums, their enumerator's values, which is implicitly convertible to Int.
#include <iostream>
using namespace std;
int main()
{
// plain enum
// underlying type is integer
// associated values can be assigned but not be larger than int
enum Color { red=12, green=20, blue=214 };
// associated values are red_card=0, green_card=1, yellow_card=2
enum Card { red_card, green_card, yellow_card };
Color color = Color::blue;
Card card = Card::green_card;
int num = color; // enum is implicitly-convertible to integral types
cout<<sizeof(num)<<endl; // sizeof enumerator is int size
// print associated values
cout<<"Color::blue="<<num<<endl;
cout<<"Card::green_card="<<card<<endl;
cout<<"Color::blue+Card::green_card="<<num+card<<endl; //not type safe
// error: enumerator value for ‘food’ must have integral or unscoped enumeration type
// enum MyEnum { food = 1.1, house='h', wife=3 };
enum MyWishList{ food = 1, house='h', wife=3 };
MyWishListe = MyEnum::house;
// print out int value, ascii value of 'h'=104
cout<<"MyWishList::house="<<e<<endl;
return 0;
}
Scoped enumeration
- the short coming of un-scoped enumeration is illustrated in line 23 of the above program. Although Color::blue and Card::green_card are of different enum, arithmetic operation could be performed on them. That is because they are not type-safe: the values are all implicitly convertible to int. The enumerator scope is the scope of the namespace that contains the enum. In this case, it is main function.
cout<<"Color::blue+Card::green_card="<<num+card<<endl;
- enum class is type-safe. Yet its value are not implicitly convertible to Int.
- Note: to print out enumerator, we must define
<< operator overload. The following print out the name of the enumerator, not the values:
#include <iostream>
enum class MyWishList{ food = 1, house='h', money=3 };
std::ostream& operator<<(std::ostream& os, const MyEnum2& e) {
switch(e) {
case MyWishList::food: os << "food"; break;
case MyWishList::house: os << "house"; break;
case MyWishList::money: os << "money"; break;
default: os.setstate(std::ios_base::failbit);
}
return os;
}
int main() {
MyWishList wish = MyEnum2::food;
std::cout << "MyWishList::food = " << wish << std::endl;
return 0;
}
References: c++ - How can I output the value of an enum class in C++11 - Stack Overflow
Kotlin Implementation
- Kotlin only provide scoped enumeration.
enum classmodifier is used to declare and define an enum. - Kotlin also provide properties such as
valueOf,name, andordinalto ouput the values, the names or the index (position) of the enumerators within the enumeration.
enum class Direction { NORTH, EAST, SOUTH, WEST }
fun main(){
val direction: Direction = Direction.EAST
// print enumerator name
println(direction.name)
// print enumerator index
println(direction.ordinal)
}
- In addition, Kotlin enum has the following extra perks:
Enumeration as array
enum class Direction { NORTH, EAST, SOUTH, WEST}
fun main() {
// access item with index 2 of the enum Direction
println(Direction.values()[2])
}
Kotlin enumeration constructor:
Kotlin enumeration can use constructor to initialize values given to enumerators (enum constant)
enum class Direction (val value: Int) {
NORTH ( 100 ),
EAST ( 200 ),
SOUTH ( 300 ),
WEST ( 400 )
}
fun main(){
val direction: Direction = Direction.EAST
// print enumerator name
println(direction.name)
// print enumerator index
println(direction.ordinal)
// print enumerator value
println(Direction.WEST.value)
}
Enum class implementing interface
Enum class is a class in Kotlin and can implement interface.
Enumerator defining Anonymous class
enum constant (enumerator) can declare their own anonymous class and methods to define the behaviours of the enumerator.