In Scala, you can write
val x = {
... do some complex computations ..
42
}
to hide stuff inside of the code block.
The closest I came in Kotlin is:
val x = {
... do some complex computations ..
42
}()
Is there a better way?
EDIT:
isn’t run {} in the above example essentially the same
is calling run costly?
ANSWER:
using run {} inlines, whereas {}() does NOT (see my own answer below)
Use the run function. It takes a function as a parameter, runs it and returns the result.
val x = run {
... do some complex computations ..
42
}
The run function is inlined, so it will have no performance overhead.
Yes there is the run function.
val x = run {
...
42
}
And also you can use these methods too:
val a=1.also{
// your code
}
val b=2.apply{
// your code}
}
val c=3.let{
// your code
}
val d=4.runCatching{
// your code
}
To verify inlining when using run, I created a small example:
fun main() {
{
Math.random()
}()
run {
Math.random()
}
}
and viewed the produced bytecode:
public final static main()V
L0
LINENUMBER 2 L0
GETSTATIC TestKt$main$1.INSTANCE : LTestKt$main$1;
CHECKCAST kotlin/jvm/functions/Function0
INVOKEINTERFACE kotlin/jvm/functions/Function0.invoke ()Ljava/lang/Object; (itf)
POP
L1
LINENUMBER 6 L1
L2
L3
L4
ICONST_0
ISTORE 0
L5
LINENUMBER 7 L5
INVOKESTATIC java/lang/Math.random ()D
L6
L7
LINENUMBER 6 L7
L8
POP2
L9
LINENUMBER 9 L9
RETURN
L10
LOCALVARIABLE $i$a$-run-TestKt$main$2 I L5 L7 0
MAXSTACK = 2
MAXLOCALS = 1
So, indeed:
Calling via () calls a generated Function0-derived class that encapsulates the block as a lambda (label L0)
Calling via run fully inlines the block (label L5)
Related
I have a function that uses http request to retrieve remote page. That page may has one or zero next page's link. If I want to generate a chain of all pages , generateSequence is an ideal solution. This is what I have done:
First , there are two utility functions :
fun getBlockingDocument(url: String): Document? , as the name suggests, it is a blocking function. The implementation is just sending HTTP request and parsing to a JSoup document.
fun getNextIndexPage(doc: Document, url: String): String? , it is also a blocking function , but it's not related to network , it just parses to get the next page, so blocking is OK here.
OK , here is the sequence code :
val initUrl = // initial url
generateSequence(tools.getBlockingDocument(initUrl).let { initUrl to it }) { (url, doc) ->
doc?.let {
parser.getNextIndexPage(doc, url)
}?.let { nextUrl ->
nextIndexUrl to tools.getBlockingDocument(nextUrl)
}
}.forEachIndexed { index, urlAndDoc ->
val url = urlAndDoc.first
logger.info("[{}] : {}", index, url)
}
It works well , and correctly chains all pages.
But what if I change the network call to a suspend function ? This is what I created :
suspend fun getSuspendingDocument(url: String): Document?
I found no similar generateSequence builder samples as flow , So I implement like this :
#ExperimentalCoroutinesApi
#Test
fun testGetAllPagesByFlow() {
val flow = flow<Pair<String, Document?>> {
suspend fun generate(url: String) {
tools.getSuspendingDocument(url)?.let { url to it }?.also { (url, doc) ->
emit(url to doc)
parser.getNextIndexPage(doc, url)?.also { nextUrl ->
generate(nextUrl) // recursive
}
}
}
generate("http://...initial url here")
} // flow
runBlocking {
flow.collectIndexed { index, urlAndDoc ->
val url = urlAndDoc.first
logger.info("[{}] : {}", index, url)
}
}
}
I uses a recursive call (fun generate()) to emit the next url found in each page. I am not sure if it is the idiomatic way to create a Flow but I found no similar codes. If you have better/idiomatic way , please tell me , thanks a lot!
Anyway , I think it should work , but my IDE (IntelliJ) complains wrong bytecode generated , which I've never seen it before.
Error:Kotlin: [Internal Error] org.jetbrains.kotlin.codegen.CompilationException: Back-end (JVM) Internal error: wrong bytecode generated
#Lorg/jetbrains/annotations/Nullable;() // invisible
// annotable parameter count: 1 (visible)
// annotable parameter count: 1 (invisible)
#Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
L0
L1
ALOAD 0
GETFIELD destiny/data/FlowTest$testGetAllPagesByFlow$flow$1.p$ : Lkotlinx/coroutines/flow/FlowCollector;
ASTORE 2
L2
L3
LINENUMBER 44 L3
NEW destiny/data/FlowTest$testGetAllPagesByFlow$flow$1$1
DUP
ALOAD 2
ALOAD 3
ACONST_NULL
INVOKESPECIAL destiny/data/FlowTest$testGetAllPagesByFlow$flow$1$1.<init> (Lkotlinx/coroutines/flow/FlowCollector;Ldestiny/data/FlowTest$testGetAllPagesByFlow$flow$1$1;Lkotlin/coroutines/Continuation;)V
ASTORE 3
L4
L5
LINENUMBER 56 L5
ALOAD 3
CHECKCAST destiny/data/FlowTest$testGetAllPagesByFlow$flow$1$1
ALOAD 0
GETFIELD destiny/data/FlowTest$testGetAllPagesByFlow$flow$1.$initUrl : Ljava/lang/String;
ALOAD 0
ALOAD 0
ALOAD 2
PUTFIELD destiny/data/FlowTest$testGetAllPagesByFlow$flow$1.L$0 : Ljava/lang/Object;
ALOAD 0
ALOAD 3
PUTFIELD destiny/data/FlowTest$testGetAllPagesByFlow$flow$1.L$1 : Ljava/lang/Object;
ALOAD 0
ICONST_1
PUTFIELD destiny/data/FlowTest$testGetAllPagesByFlow$flow$1.label : I
INVOKEVIRTUAL destiny/data/FlowTest$testGetAllPagesByFlow$flow$1$1.invoke (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
L6
DUP
ALOAD 4
IF_ACMPNE L7
L8
LINENUMBER 42 L8
ALOAD 4
ARETURN
L9
ALOAD 0
GETFIELD destiny/data/FlowTest$testGetAllPagesByFlow$flow$1.L$1 : Ljava/lang/Object;
CHECKCAST destiny/data/FlowTest$testGetAllPagesByFlow$flow$1$1
ASTORE 3
ALOAD 0
GETFIELD destiny/data/FlowTest$testGetAllPagesByFlow$flow$1.L$0 : Ljava/lang/Object;
CHECKCAST kotlinx/coroutines/flow/FlowCollector
ASTORE 2
L10
ALOAD 1
INVOKESTATIC kotlin/ResultKt.throwOnFailure (Ljava/lang/Object;)V
ALOAD 1
L7
LINENUMBER 58 L7
POP
L11
L12
GETSTATIC kotlin/Unit.INSTANCE : Lkotlin/Unit;
ARETURN
L13
L14
L15
NEW java/lang/IllegalStateException
DUP
LDC "call to 'resume' before 'invoke' with coroutine"
INVOKESPECIAL java/lang/IllegalStateException.<init> (Ljava/lang/String;)V
ATHROW
RETURN
L16
LOCALVARIABLE $this$flow Lkotlinx/coroutines/flow/FlowCollector; L2 L14 2
LOCALVARIABLE $fun$generate$1 Ldestiny/data/FlowTest$testGetAllPagesByFlow$flow$1$1; L4 L11 3
LOCALVARIABLE this Ldestiny/data/FlowTest$testGetAllPagesByFlow$flow$1; L0 L13 0
LOCALVARIABLE $result Ljava/lang/Object; L0 L13 1
MAXSTACK = 5
MAXLOCALS = 4
File being compiled at position: (42,46) in /destiny/data/core/src/test/java/destiny/data/FlowTest.kt
The root cause org.jetbrains.kotlin.codegen.CompilationException was thrown at: org.jetbrains.kotlin.codegen.TransformationMethodVisitor.visitEnd(TransformationMethodVisitor.kt:92)
at org.jetbrains.kotlin.codegen.FunctionCodegen.endVisit(FunctionCodegen.java:990)
at org.jetbrains.kotlin.codegen.FunctionCodegen.generateMethodBody(FunctionCodegen.java:487)
at org.jetbrains.kotlin.codegen.FunctionCodegen.generateMethod(FunctionCodegen.java:260)
at org.jetbrains.kotlin.codegen.FunctionCodegen.generateMethod(FunctionCodegen.java:176)
...
It's very long , the remainings are omitted.
What's wrong with this code ?
And if the recursive way is not ideal , is there any better solution ? (like generateSequence , it is beautiful) . Thanks.
Environments :
<kotlin.version>1.3.50</kotlin.version>
<kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
<version>1.3.2</version>
</dependency>
IntelliJ 2018.3.6
$ java -version
java version "11.0.3" 2019-04-16 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.3+12-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.3+12-LTS, mixed mode)
Why use recursion at all? The following code would do the same
val f: Flow<Pair<String, Document?>> = flow {
var nextUrl: String? = url
while (nextUrl != null) {
val doc = tools.getSuspendingDocument(nextUrl)
emit(url to doc)
if (doc == null) break;
nextUrl = parser.getNextIndexPage(doc, url)
}
}
From what I understand, the big advantage of ?.let{} over != null is it guarantees that a mutable value is not changed inside the block.
However, in case of an immutable variable is there a performance difference?
For example, I have a simple method
private fun test() {
val x: String? = ""
if (x != null) {
print("test")
}
x?.let {
print("test")
}
}
When I see the resulting Kotlin Bytecode it seems that for let it has much more code.
For the x != null case:
LINENUMBER 8 L1
L2
LINENUMBER 9 L2
LDC "test"
ASTORE 2
L3
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 2
INVOKEVIRTUAL java/io/PrintStream.print (Ljava/lang/Object;)V
L4
L5
For x?.let { } case it is:
LINENUMBER 12 L5
ALOAD 1
ASTORE 2
L6
L7
ALOAD 2
ASTORE 3
L8
ICONST_0
ISTORE 4
L9
LINENUMBER 13 L9
LDC "test"
ASTORE 5
L10
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 5
INVOKEVIRTUAL java/io/PrintStream.print (Ljava/lang/Object;)V
L11
L12
LINENUMBER 14 L12
L13
NOP
L14
LINENUMBER 12 L14
L15
NOP
L16
If I decompile to java then the resulting code seems similar with one more variable being assigned for let (curiously an int variable is set to false)
For x != null:
String var2 = "test";
System.out.print(var2);
For x?.let { }
int var4 = false;
String var5 = "test";
System.out.print(var5);
In the end, my question is: Is there a performance different between let and != for immutable variables?
The two statements are not really equivalent. The outcome is obviously the same in your two examples, but let is a scope function and thus is doing a bit more lifting than simple flow control.
From the linked documentation:
The Kotlin standard library contains several functions whose sole purpose is to execute a block of code within the context of an object. When you call such a function on an object with a lambda expression provided, it forms a temporary scope.
For example even if you're not using the it in your example, this context with the it-variable is still created and that bears some overhead with it.
However, as others have pointed out I think this is a case where it's better to optimize the code for readability over speed in this case. In cases where a let allows for more readable code (which IMO is usually the case), that's a bigger win than the miniscule performance gain you may get from using an if instead.
I have the following function that uses TCO:
tailrec fun superDigit(n: String): Int {
val sum = n.fold(0) { sum, char -> sum + char.toString().toInt() }
return if (sum < 10) sum else superDigit(sum.toString())
}
If I implement the same function as an extension function like this:
fun String.superDigit(): Int {
val sum = fold(0) { sum, char -> sum + char.toString().toInt() }
return if (sum < 10) sum else sum.toString().superDigit()
}
Is the extension function tail call optimized as well?
IMO calling the extension function is still a regular function call with this as parameter so it's still a recursive call and since tailrec can't be used in extension functions, my assumption is that it's not been optimized by the compiler. Is this assumption correct or not?
since tailrec can't be used in extension functions
Are you sure?
I've just tested it by taking a look at Kotlin bytecode in IntelliJ IDEA. First of all, the code with tailrec on the extension function compiles successfully. Going further: compare the two pieces of Kotlin code and bytecode below, one with tailrec, and the other one without.
Kotlin:
fun Double.tailrecTestExtension(): Double
= (this - 1.0).tailrecTestExtension()
Bytecode:
// access flags 0x19
public final static tailrecTestExtension(D)D
// annotable parameter count: 1 (visible)
// annotable parameter count: 1 (invisible)
L0
LINENUMBER 13 L0
DLOAD 0
DCONST_1
DSUB
INVOKESTATIC com/example/TestKt.tailrecTestExtension (D)D
DRETURN
L1
LOCALVARIABLE $receiver D L0 L1 0
MAXSTACK = 4
MAXLOCALS = 2
Kotlin:
tailrec fun Double.tailrecTestExtension(): Double
= (this - 1.0).tailrecTestExtension()
Bytecode:
// access flags 0x19
public final static tailrecTestExtension(D)D
// annotable parameter count: 1 (visible)
// annotable parameter count: 1 (invisible)
L0
LINENUMBER 13 L0
DLOAD 0
DCONST_1
DSUB
DSTORE 0
GOTO L0
L1
LOCALVARIABLE $receiver D L0 L1 0
MAXSTACK = 4
MAXLOCALS = 2
Notice that in the first example there's INVOKESTATIC call (which corresponds to a regular recursion), which was replaced with a regular jump (GOTO) in the second version (which corresponds to a loop - expected behavior introduced by tailrec).
Note: I'm not an expert in Kotlin bytecode, my understanding is based on some basic knowledge about the assembly language. Here I assume that this knowledge is transferable to Kotlin bytecode.
I know inline keyword means to avoid the call overhead calling a funtion. But I can't figure out what inline a extension property work for?
Let say we have two extension property named foo and another with is inlined named bar
val Any.foo : Long
get() = Date().time
inline val Any.bar : Long
get() = Date().time
Executing any of them, we gent the expected output, the current time.
The bytecode for this file is this below:
public final class InlinedExtensionPropertyKt {
public final static getFoo(Ljava/lang/Object;)J
#Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
L0
ALOAD 0
LDC "$receiver"
INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
L1
LINENUMBER 9 L1
NEW java/util/Date
DUP
INVOKESPECIAL java/util/Date.<init> ()V
INVOKEVIRTUAL java/util/Date.getTime ()J
LRETURN
L2
LOCALVARIABLE $receiver Ljava/lang/Object; L0 L2 0
MAXSTACK = 2
MAXLOCALS = 1
public final static getBar(Ljava/lang/Object;)J
#Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
L0
ALOAD 0
LDC "$receiver"
INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
L1
LINENUMBER 12 L1
NEW java/util/Date
DUP
INVOKESPECIAL java/util/Date.<init> ()V
INVOKEVIRTUAL java/util/Date.getTime ()J
LRETURN
L2
LOCALVARIABLE $receiver Ljava/lang/Object; L0 L2 0
LOCALVARIABLE $i$f$getBar I L0 L2 1
MAXSTACK = 2
MAXLOCALS = 2
#Lkotlin/Metadata;(mv={1, 1, 7}, bv={1, 0, 2}, k=2, d1={"\u0000\u000e\n\u0000\n\u0002\u0010\u0009\n\u0002\u0010\u0000\n\u0002\u0008\u0005\"\u0016\u0010\u0000\u001a\u00020\u0001*\u00020\u00028\u00c6\u0002\u00a2\u0006\u0006\u001a\u0004\u0008\u0003\u0010\u0004\"\u0015\u0010\u0005\u001a\u00020\u0001*\u00020\u00028F\u00a2\u0006\u0006\u001a\u0004\u0008\u0006\u0010\u0004\u00a8\u0006\u0007"}, d2={"bar", "", "", "getBar", "(Ljava/lang/Object;)J", "foo", "getFoo", "test sources for module app"})
// compiled from: InlinedExtensionPropertyKt.kt
}
We can see both are similar but differents only on these lines:
foo extract:
LOCALVARIABLE $receiver Ljava/lang/Object; L0 L2 0
MAXSTACK = 2
MAXLOCALS = 1
bar extract:
LOCALVARIABLE $receiver Ljava/lang/Object; L0 L2 0
LOCALVARIABLE $i$f$getBar I L0 L2 1
MAXSTACK = 2
MAXLOCALS = 2
I really don't understand what is happennig here.
Can someone point me to see what is the behaviour, or the equivalent in java, or some use for this?
Edit
Given the compiler will replace the content of inlined property, it may convenient to inline every extension property having not heavy operations ?
Thank you
From Kotlin's doc,
Note that, since extensions do not actually insert members into classes, there's no efficient way for an extension property to have a backing field.
and also,
The inline modifier can be used on accessors of properties that don't have a backing field.
As mentioned above, an inline extension property does not have a backing field. You may treat an extension property as a pair of static getter/setter, like this:
//In Kotlin
var Any.foo : Long
get() = Date().time
set(value) {
//Cannot access field here since extension property cannot have backing field
//Do something with `obj`
}
//In Java
public static long getFoo(Object obj) {
return new Date().getTime();
}
public static void setFoo(Object obj) {
//Do something with `obj`
}
So, inline property means that the code of the getter/setter function will be inlined into the call site when accessing the property (same as regular inline functions).
//In Kotlin
val x = "".foo
val y = "".bar
//Generated code
val x = InlinedExtensionPropertyKt.getFoo("")
val y = Date().time
For the bytecode that you post in the question, sorry that I am not able explain what is happening. But you may try to take a look at the bytecode of the following code:
fun get() {
val x = "".foo
val y = "".bar
}
, where "".foo will invoke the getter function but "".bar will not.
I am still learning Kotlin and trying to understand its core principles. What I do not get is this:
fun x() : Int { return 10 }
val y : () -> Int = ::x
val z : () -> Int = { 10 }
fun main(args: Array<String>) {
println(::x)
println(y)
println(z)
}
we get the following output:
fun x(): kotlin.Int
fun x(): kotlin.Int
() -> kotlin.Int
My question is why the output is not the same (I believed these functions should be interchangeable, equivalent)? I think the type of all the functions should be () -> Int. Why do we keep the original name with the function signature (fun x) even though it is assigned to a different name (y)? Is there any language design principle which would require such difference in function signatures?
And a bonus question - why do we need to use the operator ::. It does not compile without it. But why is this required by the language design? Would not val y = x work just fine and be much simpler?
fun x() is an ordinary named function from procedural programming. val y is a property which holds a reference to x. val z is an anonymous functional from functional programming.
:: is a 'function reference', a kind of bridge between procedural and functional programming.
By default, your functions should be fun. Lambda expressions (anonymous functions), on the other side, are intended to be passed to other functions as callbacks.
IF you think from my point of view, I think you can understand it immediately.
x just is an identifier not a variable, so you can't reference it directly.
fun x() is a class which derived from Function0.
the expression ::x is the instance of the type of fun x(), this is called function reference expression in kotlin.
Kotlin makes functions and properties first-class citizens in the language, and introspecting them.
sometimes function has it own class in kotlin like as java-8 lambda expression. but function reference expression can't used with the diff receiver.
when calling an inline function followed with a lambda, both the lambda & the function will be inlined at call-sites. but call with a non-inline function reference expression, only the inline function will be inlined at call-sites.
I think for better understanding look at bytecode
private final static Lkotlin/jvm/functions/Function0; y
#Lorg/jetbrains/annotations/NotNull;() // invisible
// access flags 0x1A
// signature Lkotlin/jvm/functions/Function0<Ljava/lang/Integer;>;
// declaration: kotlin.jvm.functions.Function0<java.lang.Integer>
private final static Lkotlin/jvm/functions/Function0; z
#Lorg/jetbrains/annotations/NotNull;() // invisible
// access flags 0x19
public final static main([Ljava/lang/String;)V
#Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
L0
ALOAD 0
LDC "args"
INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
L1
LINENUMBER 6 L1
GETSTATIC MainKt$main$1.INSTANCE : LMainKt$main$1;
ASTORE 1
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 1
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
L2
LINENUMBER 7 L2
GETSTATIC MainKt.y : Lkotlin/jvm/functions/Function0;
ASTORE 1
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 1
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
L3
LINENUMBER 8 L3
GETSTATIC MainKt.z : Lkotlin/jvm/functions/Function0;
ASTORE 1
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 1
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
L4
LINENUMBER 9 L4
RETURN
L5
LOCALVARIABLE args [Ljava/lang/String; L0 L5 0
MAXSTACK = 2
MAXLOCALS = 2
// access flags 0x19
public final static x()I
L0
LINENUMBER 11 L0
BIPUSH 10
IRETURN
MAXSTACK = 1
MAXLOCALS = 0
// access flags 0x19
// signature ()Lkotlin/jvm/functions/Function0<Ljava/lang/Integer;>;
// declaration: kotlin.jvm.functions.Function0<java.lang.Integer> getY()
public final static getY()Lkotlin/jvm/functions/Function0;
#Lorg/jetbrains/annotations/NotNull;() // invisible
L0
LINENUMBER 12 L0
GETSTATIC MainKt.y : Lkotlin/jvm/functions/Function0;
ARETURN
MAXSTACK = 1
MAXLOCALS = 0
// access flags 0x19
// signature ()Lkotlin/jvm/functions/Function0<Ljava/lang/Integer;>;
// declaration: kotlin.jvm.functions.Function0<java.lang.Integer> getZ()
public final static getZ()Lkotlin/jvm/functions/Function0;
#Lorg/jetbrains/annotations/NotNull;() // invisible
L0
LINENUMBER 13 L0
GETSTATIC MainKt.z : Lkotlin/jvm/functions/Function0;
ARETURN
MAXSTACK = 1
MAXLOCALS = 0
// access flags 0x8
static <clinit>()V
L0
LINENUMBER 12 L0
GETSTATIC MainKt$y$1.INSTANCE : LMainKt$y$1;
CHECKCAST kotlin/jvm/functions/Function0
PUTSTATIC MainKt.y : Lkotlin/jvm/functions/Function0;
L1
LINENUMBER 13 L1
GETSTATIC MainKt$z$1.INSTANCE : LMainKt$z$1;
CHECKCAST kotlin/jvm/functions/Function0
PUTSTATIC MainKt.z : Lkotlin/jvm/functions/Function0;
RETURN
MAXSTACK = 1
MAXLOCALS = 0
}
y and z value have type Function0
static <clinit>()V // here we have static initialization block where y and z are initialized
x have type MainKt$main$1
In each case(println) we just show method declaration(visibility modifiers, return type, signature) without invocation.
y and z are high order function that in bytecode are presented by Function0 class, for x value it's just static function.
And when you invoke println(::x) println(y) println(z) you just print functions declarations. This is ok, that declaration of x and y functions are different that in z function. Because y has reference to x function and z are still high order function that return 10. In simple words y == x, because you assign to y function declaration from x.
Just for Note. In Java I can show method declaration in this way:
public static void main(String[] args) {
try {
System.out.println(Main.class.getMethod("A", null)); // prints public static void Main.A()
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
public static void A() {
System.out.println(15);
}
Conclusion:
In your code you just print function return type, visibility modifier and signature.