Inner classes: Difference between revisions
Promoted to ‘full’ task. |
New post. |
||
Line 308: | Line 308: | ||
box |
box |
||
</syntaxhighlight> |
</syntaxhighlight> |
||
=={{header|Java}}== |
|||
<syntaxhighlight lang="java"> |
|||
public final class InnerClasses { |
|||
public static void main(String[] args) { |
|||
/** |
|||
In Java, inner classes are used to increase encapsulation |
|||
and to logically group together classes that will only be used in one place. |
|||
This can lead to more readable and maintainable code. |
|||
For further information visit: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html |
|||
The most common use of inner classes is create an Iterator or Comparator for the outer class. |
|||
For example, the Java language source code uses an inner class to provide a Comparator for the String class. |
|||
A good example of inner class use is that the Set and List classes have different implementations of |
|||
an inner Iterator class. |
|||
*/ |
|||
OuterClass outer = new OuterClass(22); |
|||
OuterClass.InnerClass inner = outer.new InnerClass(20); |
|||
System.out.println(inner.totalValue()); |
|||
/** |
|||
The inner class cannot be instantiated without using an instance of the outer class, |
|||
unless we have defined a static inner class. |
|||
*/ |
|||
} |
|||
} |
|||
// Define an outer class |
|||
final class OuterClass { |
|||
// Constructor for Outer class |
|||
public OuterClass(int aValue) { |
|||
outerValue = aValue; |
|||
} |
|||
// Define an inner class |
|||
final class InnerClass { |
|||
// Constructor for Inner class |
|||
public InnerClass(int aValue) { |
|||
innerValue = aValue; |
|||
} |
|||
// The inner class has access to the private fields of the outer class |
|||
public int totalValue() { |
|||
return outerValue + innerValue; |
|||
} |
|||
private int innerValue; |
|||
} |
|||
private int outerValue; |
|||
} |
|||
</syntaxhighlight> |
|||
{{ out }} |
|||
<pre> |
|||
42 |
|||
</pre> |
|||
=={{header|jq}}== |
=={{header|jq}}== |
Latest revision as of 16:16, 15 July 2024
![Task](http://static.miraheze.org/rosettacodewiki/thumb/b/ba/Rcode-button-task-crushed.png/64px-Rcode-button-task-crushed.png)
You are encouraged to solve this task according to the task description, using any language you may know.
- Definition
In object-oriented programming, an inner or nested class is a class declared entirely within the body of another class or interface.
It is not the same as a subclass which is a class which inherits members from another class or classes and can be declared anywhere the parent class(es) are within scope. However, inner classes may nevertheless be subclasses of other classes as well.
- Task
If your language supports the object oriented paradigm, explain what support it has for inner classes and, if there is no support, what if anything can be done to simulate them.
Illustrate your answer by creating an inner class with a constructor, an instance method and field and show how to instantiate it.
If your language does not support the object oriented paradigm, you may either omit this task or describe (and substitute in your example) any equivalent structure(s) which the language does have.
- Reference
- Related task
ALGOL 68
Algol 68 doesn't do OO as such but it can be simulated, one possible method is demonstrated here.
Note that in Algol 68 references to structure members are written as a OF b
, whereas most languages would use b.a
.
This tends to require more brackets: a OF b( i )
selects the field a from b( i ), ( a OF b )( i )
calls the member procedure a with parameter i.
This example does not hide the details of the INNER "class" from the program. Also, the INNER structure is declared outside of the OUTER structure, as references to OUTER are required in the definition of INNER (and vice-versa), which would make it hard to define an anonymous INNER in OUTER.
As there is no OO syntax, the this
and super
references have to be passed as explicit parameters to the methods and constructors. The call to the print method of o for example, has to be written as ( print OF o )( o )
. Writing print OF o
won't call the method as no "this" reference has been passed to it.
In this example, the OUTER structure contains a reference to an INNER instance. This is not required in general, the oinner field could be omitted (and the code using it) if not needed.
BEGIN # demonstrate a possible method of simulating inner classes #
# declare OUTER and INNER "classes" #
MODE OUTER = STRUCT( INT omember # integer member #
, PROC REF REF OUTER new0 # noarg constructer #
, REF REF INNER oinner # INNER member #
, PROC( REF OUTER )VOID print # print method #
);
MODE INNER = STRUCT( REF REF OUTER super # parent OUTER #
, CHAR cmember # character member #
, PROC( REF REF OUTER )REF REF INNER
new0 # noarg constructor #
# but must be called withe the superclass "pointer" #
, PROC( REF INNER )VOID print # print method #
);
# constructors #
PROC new outer = REF REF OUTER:
BEGIN
HEAP OUTER thisv := OUTER( 0
, new outer
, NIL
, ( REF OUTER this )VOID:
print( ( "[", whole( omember OF this, 0 ), "]" ) )
);
HEAP REF OUTER this := thisv;
oinner OF this := new inner( this );
this
END # new outer # ;
PROC new inner = ( REF REF OUTER super )REF REF INNER:
BEGIN
HEAP INNER thisv := INNER( super
, " "
, new inner
, ( REF INNER this )VOID:
print( ( "<<", cmember OF this
, "><", whole( omember OF super OF this, 0 )
, ">>"
)
)
);
HEAP REF INNER this := thisv;
this
END # new inner # ;
REF OUTER o := new outer; # create an instance of OUTER #
omember OF o := 27; # set o's integer member #
cmember OF oinner OF o := "-"; # set o's inner member's character member #
( print OF oinner OF o )( oinner OF o ); # call o's INNER members' print #
( print OF o )( o ); # call o's print #
print( ( newline ) );
omember OF o := 42; # update o's integer member #
cmember OF oinner OF o := "!"; # and its inner member's character member #
( print OF oinner OF o )( oinner OF o ); # call o's INNER members' print #
( print OF o )( o ); # call o's print #
print( ( newline ) );
REF INNER i := new inner( o ); # create a separate INNER instance also #
# with parent o #
cmember OF i := "$"; # update the separate INNER's character member #
( print OF i )( i ); # call the separate INNER's print method #
( print OF o )( o ); # call o's print method again #
print( ( newline ) );
omember OF o := 91; # update o's integer member ... #
cmember OF oinner OF o := "?"; # and its INNER member's character member #
( print OF oinner OF o )( oinner OF o ); # call o's INNER members' print #
( print OF o )( o ); # call o's print method again #
print( ( newline ) );
( print OF i )( i ); # call the separate INNER's print method again #
( print OF o )( o ); # call o's print method again #
print( ( newline ) )
END
- Output:
<<-><27>>[27] <<!><42>>[42] <<$><42>>[42] <<?><91>>[91] <<$><91>>[91]
C++
C++ supports nested/inner classes. There is no difference but the term nested is more common.
#include <iostream>
#include <vector>
class Outer
{
int m_privateField;
public:
// constructor for Outer class
Outer(int value) : m_privateField{value}{}
// define a nested class
class Inner
{
int m_innerValue;
public:
// constructor for Inner class
Inner(int innerValue) : m_innerValue{innerValue}{}
// adds the values from the outer and inner class objects
int AddOuter(Outer outer) const
{
// a nested class has access to the private members of the outer class
return outer.m_privateField + m_innerValue;
}
};
};
int main()
{
// a nested class can be constructed like any other class; it does not
// need an instance of the outer class
Outer::Inner inner{42};
// create an outer class and pass it to the inner class
Outer outer{1};
auto sum = inner.AddOuter(outer);
std::cout << "sum: " << sum << "\n";
// a common usage of nested types is for containers to define their iterators
std::vector<int> vec{1,2,3};
std::vector<int>::iterator itr = vec.begin();
std::cout << "vec[0] = " << *itr << "\n";
}
- Output:
sum: 43 vec[0] = 1
FreeBASIC
FreeBASIC is not an object-oriented programming language and does not have support for internal classes. However, you can simulate some features of object-oriented programming using Type and Sub.
Type Inner
campo As Integer
End Type
Type Outer
campo As Integer
interno As Inner
End Type
Sub outerMethod(o As Outer)
Print "Outer's field has a value of "; o.field
End Sub
Sub innerMethod(o As Outer)
Print "Inner's field has a value of "; o.interno.field
End Sub
Dim As Outer o
o.field = 43
o.interno.field = 42
innerMethod(o)
outerMethod(o)
Dim As Outer p
p.field = 45
p.interno.field = 44
innerMethod(p)
outerMethod(p)
Sleep
- Output:
Same as Go entry.
Go
Go supports some aspects of OO albeit in an unorthodox manner:
- Structs are used rather than classes.
- Any methods are always defined outside the struct itself but within the containing package.
- Struct fields are never private to the struct itself though, unless they begin with an upper case letter, they are private to the containing package.
- Structs do not support inheritance - composition can be used instead.
- Polymorphism is supported to some extent via the use of interfaces.
It is possible to nest structs as the following example shows. However, the problem here is that the nested struct has an anonymous type and the only way to give it methods is via an instance of the Outer struct.
In practice it would probably be better to declare the nested struct independently but within the same package and achieve encapsulation by restricting the package to just those two structs.
package main
import "fmt"
type Outer struct {
field int
Inner struct {
field int
}
}
func (o *Outer) outerMethod() {
fmt.Println("Outer's field has a value of", o.field)
}
func (o *Outer) innerMethod() {
fmt.Println("Inner's field has a value of", o.Inner.field)
}
func main() {
o := &Outer{field: 43}
o.Inner.field = 42
o.innerMethod()
o.outerMethod()
/* alternative but verbose way of instantiating */
p := &Outer{
field: 45,
Inner: struct {
field int
}{
field: 44,
},
}
p.innerMethod()
p.outerMethod()
}
- Output:
Inner's field has a value of 42 Outer's field has a value of 43 Inner's field has a value of 44 Outer's field has a value of 45
J
J allows inner classes, but because such classes are always public, there is little or no utility to be had, nesting classes in J.
Class definition for task:
coclass 'baggagecheck'
create=: {{
data=: keys=: i.0
mysn=: '' conew 'serialnumber'
}}
create_serialnumber_=: {{number=: 0}}
destroy_serialnunber_=: codestroy
get_serialnumber_=: {{number=: 1+number}}
destroy=: {{
destroy__mysn ''
codestroy ''
}}
checkin=: {{
sn=.get__mysn''
data=: data,<y
keys=: keys,sn
sn
}}
checkout=: {{
r=.>(keys i. y){data
b=. keys~:y
data=: b#data
keys=: b#keys
r
}}
Example use:
checker=: '' conew 'baggagecheck'
checkin__checker 'suitcase'
1
checkin__checker 'box'
2
checkin__checker 'bicycle'
3
checkout__checker 2
box
Java
public final class InnerClasses {
public static void main(String[] args) {
/**
In Java, inner classes are used to increase encapsulation
and to logically group together classes that will only be used in one place.
This can lead to more readable and maintainable code.
For further information visit: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
The most common use of inner classes is create an Iterator or Comparator for the outer class.
For example, the Java language source code uses an inner class to provide a Comparator for the String class.
A good example of inner class use is that the Set and List classes have different implementations of
an inner Iterator class.
*/
OuterClass outer = new OuterClass(22);
OuterClass.InnerClass inner = outer.new InnerClass(20);
System.out.println(inner.totalValue());
/**
The inner class cannot be instantiated without using an instance of the outer class,
unless we have defined a static inner class.
*/
}
}
// Define an outer class
final class OuterClass {
// Constructor for Outer class
public OuterClass(int aValue) {
outerValue = aValue;
}
// Define an inner class
final class InnerClass {
// Constructor for Inner class
public InnerClass(int aValue) {
innerValue = aValue;
}
// The inner class has access to the private fields of the outer class
public int totalValue() {
return outerValue + innerValue;
}
private int innerValue;
}
private int outerValue;
}
- Output:
42
jq
Works with jq, the C implementation of jq
Works with gojq, the Go implementation of jq
jq does not support classes or inner classes as such, but it does support inner functions, which in turn support inner functions, and so on recursively. These provide a somewhat similar organizational mechanism.
An inner function is invisible outside the immediately enclosing function, but can call itself or any previously declared function that is visible.
The following program illustrates among other things how an inner function (named Inner1) can be written so as to access the outer function's data -- here, a JSON object with a key named "field" -- much as in the Wren example. Here, however, the name "super" is just a convention.
Notice that the inner function `IncrementField` is called both by Inner1 (where it affects only `.field` local to Inner1) and by Inner2 (where it modifies `.field` local to Outer).
In order to see how things work, we'll define a "probe" for printing messages (to stderr) as follows:
def probe(msg): . as $in | msg + "\n" | stderr | $in;
def Outer:
def IncrementField: .field += 1;
{field: 123}
| def Inner1:
{super: .,
field: 456}
| IncrementField
| probe("Inner1's field is \(.field) and can see the super value is \(.super.field)")
| .super ; # avoid contamination
def Inner2:
IncrementField
| probe("Inner2 has altered the value of .field which is now \(.field)");
probe("Outer field's value is initially \(.field)")
| Inner1
| .field = 0
| probe("Outer field's value has been changed to \(.field)")
| Inner1
| probe("Calling Inner2")
| Inner2
;
Outer
- Output:
Outer field's value is initially 123 Inner1's field is 457 and can see the super value is 123 Outer field's value has been changed to 0 Inner1's field is 457 and can see the super value is 0 Calling Inner2 Inner2 has altered the value of .field which is now 1 { "field": 1 }
Julia
Julia allows inner classes, but because such classes are always public, there is little or no utility to be had nesting classes in Julia. Private methods in Julia are best simulated via modules which limit their exports.
Anything that can be done with inner classes can be accomplished in Julia just as well with the structs not nested, and the non-nested versions may be easier to understand. Furthermore, the new() function used for inner constructors in Julia refers only to the outermost struct, so the inner class must have any non-implict constructors that may be needed defined outside the struct.
""" see the Outer class in C++ example """
struct Outer
m_privateField::Int
""" Inner class in example """
struct Inner
m_innerValue::Int
end
end
""" adds the values from the outer and inner class objects """
addouter(inner::Inner, outer::Outer) = outer.m_privateField + inner.m_innerValue
"""
Test the functions. Iterables in Julia are structs for which the iterate()
function is defined, so no need for inner classes for that
"""
function main()
inner = Inner(42)
outer = Outer(1)
outplusin = addouter(inner, outer)
println("sum: $outplusin")
end
main()
Phix
Phix has no support whatsoever for inner classes. You can of course do something like this:
without js -- (classes) class inner public integer v function is(integer v) return v+this.v end function end class class outer public integer v public inner i function ois() inner ti = this.i -- (a needed compiler hint) return ti.is(v) end function end class outer o = new({42,new(inner,{1})}) ?o.v ?o.i.v ?o.v+o.i.v ?o.ois()
A shown, Phix often needs explicit compiler hints when dealing with classes, without "ti" above it treats "this.i" as still somehow being an "outer", and admittedly there is no real access to outer's contents within inner. Instead you would need shims like that ois() to explicitly pass along everything it needs.
It is also possible to pass routines around directly, such as i.is and o.ois [ie no parenthesis] and then invoke them with an explicit "this", which can get round some of those limitations, and act as an alternative to those compiler hints.
- Output:
42 1 43 43
Raku
Raku supports nested classes, however they are really only useful for grouping units of behavior together into a namespace. There is no particular benefit as far as access to private methods or attributes that can't be achieved by non-nested classes. Access to inner classes must use fully qualified paths.
class Outer {
has $.value is rw;
method double { self.value ×= 2 }
class Inner {
has $.value is rw;
}
}
# New Outer instance with a value of 42.
my $outer = Outer.new(:value(42));
say .^name, ' ', .value given $outer;
$outer.double;
say .^name, ' ', .value given $outer;
# New Inner instance with no value set. Note need to specify fully qualified name.
my $inner = Outer::Inner.new;
# Set a value after the fact.
$inner.value = 16;
# There is no way for the Inner object to call the Outer .double method.
# It is a separate, distinct class, just in a funny namespace.
say .^name, ' ', .value given $inner;
$inner.value /=2;
say .^name, ' ', .value given $inner;
- Output:
Outer 42 Outer 84 Outer::Inner 16 Outer::Inner 8
Wren
Strictly speaking, Wren does not support inner classes.
However, it is possible to declare a class within either a method or function and we can use this feature to simulate an inner class.
To make things a little more interesting the inner class in the following example also inherits from the outer class. In fact inheritance is the only way for the inner class to access the outer class's instance methods directly as it is otherwise treated the same as a non-nested class. The only advantage it really offers is to encapsulate closely related classes under the one umbrella .
class Outer {
static makeInner {
class Inner is Outer {
construct new(field) {
super(field + 1) // call parent class constructor
_field = field
}
method {
System.print("Inner's field has a value of %(_field)")
outerMethod
}
}
return Inner
}
construct new(field) {
_field = field
}
outerMethod {
System.print("Outer's field has a value of %(_field)")
}
}
var Inner = Outer.makeInner
var inner = Inner.new(42)
inner.method
- Output:
Inner's field has a value of 42 Outer's field has a value of 43