Kotlin: Possible to modify functions during compile time through metaprogramming?

限于喜欢 提交于 2019-12-05 09:13:49

As stated in my comment: in almost all cases, it's more desirable to use an appropriate design pattern than to start relying on things like dynamic proxies, reflection, or AOP to address this kind of problem.

That being said, the question asks whether it's possible to modify Kotlin functions at compile time through meta-programming, and the answer is "Yes". To demonstrate, below is a complete example that uses AspectJ.


Project structure

I set up a small Maven-based project with the following structure:

.
├── pom.xml
└── src
    └── main
        └── kotlin
            ├── Aop.kt
            └── Main.kt

I'll reproduce the contents of all files in the sections below.


Application code

The actual application code is in the file named Main.kt, and—except for the fact that I renamed your function to be in line with Kotlin naming rules—it's identical to the code provided in your question. The getNumber() method is designed to return 3.

fun main(args: Array<String>) {
    println(getNumber())
}

fun getNumber(): Int {
    return 3
}

AOP code

The AOP-related code is in Aop.kt, and is very simple. It has an @Around advice with a point cut that matches the execution of the getNumber() function. The advice will intercept the call to the getNumber() method and return 42 (instead of 3).

import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect

@Aspect
class Aop {
    @Around("execution(* MainKt.getNumber(..))")
    fun getRealNumber(joinPoint: ProceedingJoinPoint): Any {
        return 42
    }
}

(Note how the name of the generated class for the Main.kt file is MainKt.)


POM file

The POM file puts everything together. I'm using 4 plugins:

This is the complete POM file:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>x.y.z</groupId>
    <artifactId>kotlin-aop</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
        <kotlin.version>1.2.61</kotlin.version>
        <aspectj.version>1.9.1</aspectj.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib</artifactId>
            <version>${kotlin.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${aspectj.version}</version>
        </dependency>
    </dependencies>
    <build>
        <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
        <plugins>
            <plugin>
                <artifactId>kotlin-maven-plugin</artifactId>
                <groupId>org.jetbrains.kotlin</groupId>
                <version>${kotlin.version}</version>
                <executions>
                    <execution>
                        <id>kapt</id>
                        <goals>
                            <goal>kapt</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>compile</id>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>com.jcabi</groupId>
                <artifactId>jcabi-maven-plugin</artifactId>
                <version>0.14.1</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>ajc</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.1.0</version>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <mainClass>MainKt</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.1.1</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Building and executing

To build, as with any Maven project, you just need to run:

mvn clean package

This will build a fat JAR at the target/kotlin-aop-1.0-SNAPSHOT.jar location. This JAR can then be executed using the java command:

java -jar target/kotlin-aop-1.0-SNAPSHOT.jar

Execution then gives us the following result, demonstrating that everything worked as expected:

42

(Application was built and executed using the most recent Oracle Java 8 JDK at the time of writing—1.8.0_181)


Conclusion

As the example above demonstrates, it's certainly possible to redefine Kotlin functions, but—to reiterate my original point—in almost all cases, there are more elegant solutions available to achieve what you need.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!