Home > Software engineering >  Kotlin: 'public' function exposes its 'private-in-class' return type argument Po
Kotlin: 'public' function exposes its 'private-in-class' return type argument Po

Time:11-08

EDIT: I HAVE PROVIDED JAVA CODE THAT SOLVES MY PROBLEM AT THE END.

I want to know how to make a private nested class in Kotlin so that it is accessible from its parent class but not accessible from anywhere else.

In the following code snippet I've marked lines where compilation error occurs with "//(!)".

class Sphere(val radius: Double = 1.toDouble()) {
    
        var pointsLinkedHashSet: LinkedHashSet<Point> = linkedSetOf() 
        //(!)'public' property exposes its 'private-in-class' type argument Point (make "Point" public)
    
        fun getPoints(): LinkedHashSet<Point> {
            return pointsLinkedHashSet
        } 
        //(!) 'public' function exposes its 'private-in-class' return type argument Point (make "Point" public)
    
        fun getLastPoint(): Point = pointsLinkedHashSet.last()
        //(!) Same as previous

        fun clearPoints() = pointsLinkedHashSet.clear()
    
        fun addPoint(radius: Double = this.radius, latitude: Double, longitude: Double): Point {
            val point = Point(radius, latitude, longitude)
            pointsLinkedHashSet.add(point)
            return point
        }
        //(!) Same as previous
    
        private class Point(
            val radius: Double,
            val latitude: Double,
            val longitude: Double
        )
    
    }

In other words, here is my Main class:

class Main {

    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            val sphere = Sphere()
            val point = sphere.addPoint(10.toDouble(), 10.toDouble(), 10.toDouble()) //this works as it should
            val point1 = sphere.getLastPoint() // this too
            val point2 = Sphere.Point(10.toDouble(), 10.toDouble(), 10.toDouble()) //this doesn't work because Point is private, as it should
            val radius = point.radius //same in these next 3 lines
            val latitude = point.latitude
            val longitude = point.longitude 
        }
    }
}

I've described desired behaviour in the code comments.

It is easy to implement in Java, but in Kotlin I can't yet find a way to do it. And couldn't find any article relevant to this problem even though it can be easily encountered.

Or am I missing something obvious? Could you help, please? Thank you.

UPDATE WITH JAVA CODE:

Sphere class:

package org.example;
import java.util.LinkedHashSet;
public class Sphere {
    private final Double radius;
    private final LinkedHashSet<Point> pointsLinkedHashSet = new LinkedHashSet<Point>();
    public Double getRadius() {
        return this.radius;
    }
    public Sphere(Double radius) {
        this.radius = radius;
    }
    public Point getLastPoint() {
        if (pointsLinkedHashSet.isEmpty()) return null;
        return (Point) pointsLinkedHashSet.toArray()[pointsLinkedHashSet.size() - 1];
    }
    public Point addPoint(Double latitude, Double longitude) {
        Point point = new Point(latitude, longitude);
        pointsLinkedHashSet.add(point);
        return point;
    }
    class Point {
        private final Double radius;
        private final Double latitude;
        private final Double longitude;
        Double getLatitude() {
            return latitude;
        }
        Double getLongitude() {
            return longitude;
        }
        Double getRadius() {
            return this.radius;
        }
        private Point(Double latitude, Double longitude) {
            this.radius = Sphere.this.radius;
            this.latitude = latitude;
            this.longitude = longitude;
        }
    }
}

Main class:

package org.example;

public class Main {
    public static void main(String[] args) {
        Sphere sphere = new Sphere(1.0);
        System.out.println("Sphere radius = "   sphere.getRadius());
        Sphere.Point point = sphere.addPoint(10.0,10.0);
        System.out.println("Point radius = "   point.getRadius());
        System.out.println("Point latitude = "   point.getLatitude());
        System.out.println("Point longitude = "   point.getLongitude());
//        Sphere.Point point = new Sphere.Point(10.0,10.0); does not work
//        can not create new Point object because its constructor is private,
//        this is desired behaviour!
        Sphere.Point point1 = sphere.getLastPoint();
        System.out.println("Point1 radius = "   point1.getRadius());
        System.out.println("Point1 latitude = "   point1.getLatitude());
        System.out.println("Point1 longitude = "   point1.getLongitude());
    }
}

Output:

Sphere radius = 1.0
Point radius = 1.0
Point latitude = 10.0
Point longitude = 10.0
Point1 radius = 1.0
Point1 latitude = 10.0
Point1 longitude = 10.0

CodePudding user response:

What was confusing about your request is that you're not asking for a private class at all. A private class is one that is completely inaccessible from outside a file, so it doesn't make sense to return it as a type from any public functions in that file. The same is true in Java.

Your Java code doesn't make Point private. It only makes its constructor private, so I am guessing what you really wanted was to prevent instantiation of the Point class from outside this file.

There does not appear to be a way to duplicate this behavior in Kotlin. (Discussion here) The closest thing would be to mark the constructor internal.

Alternatively, it is possibly sufficient for you to mark the class as inner, so the class cannot be instantiated without the context of the outer class. You could call someSphereInstance.Point(...), but not Sphere.Point(...).

Note that making the nested class inner will make it more like your Java code. The two types of nested class are declared differently in Java and Kotlin:

Modifier keywords for nested classes:

Bound to an outer class instance Independent of outer instance
Java none static
Kotlin inner none
  • Related