Inner classes: Difference between revisions

From Rosetta Code
Content added Content deleted
m (Fixed typos.)
(→‎{{header|Wren}}: Added some remarks to preamble about accessing outer class methods.)
Line 206: Line 206:
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.
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.
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 .
<syntaxhighlight lang="ecmascript">class Outer {
<syntaxhighlight lang="ecmascript">class Outer {
static makeInner {
static makeInner {

Revision as of 19:23, 29 January 2023

Inner classes is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
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

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