Is there a way override function with kotlin extension - android

We can override function on Swift programming like below.
// ---------- BaseClass.swift -------------
public class BaseClass
{
public var method1:(Int) -> String { return doMethod1 }
public init() {}
}
// the extension could also be in a separate file
extension BaseClass
{
private func doMethod1(param:Int) -> String { return "BaseClass \(param)" }
}
// ---------- ClassA.swift ----------
public class A:BaseClass
{
override public var method1:(Int) -> String { return doMethod1 }
}
// this extension can be in a separate file but not in the same
// file as the BaseClass extension that defines its doMethod1 implementation
extension A
{
private func doMethod1(param:Int) -> String
{
return "A \(param) added to \(super.method1(param))"
}
}
// ---------- ClassB.swift ----------
public class B:A
{
override public var method1:(Int) -> String { return doMethod1 }
}
extension B
{
private func doMethod1(param:Int) -> String
{
return "B \(param) added to \(super.method1(param))"
}
}
But I try to call fun BaseClass.method1 in Kotlin like below;
class BaseClass {
fun method1(){
println("method1")
}
}
fun main() {
fun BaseClass.method1(){
println("method12")
}
BaseClass().method1()
}
It's print method1. If I add override, shows Modifier 'override' is not applicable to 'local function' error. Does anyone have a solution on this issue. I want to replace some features with the extension function without changing the original.

You can make your own extension function using the original one.
F.E.
Extension function:
fun String.toInt(){
this.toInt()
}
But you want to get the abs value
fun String.toPositiveInt(){
this.toInt().abs()
}
So I'm using the original toInt with a modification to get the number without the symbol.

Related

Why is the method registerTransform not found in Android ASM bytecode manipulation?

I'm trying to implement a transform using ASM, in order to replace method like Log.d to MyLog.d. But registerTransform is not found, can anyone tell me what is wrong with my demo project? (https://github.com/thelou1s/android_kotlin_asm.git)
class SystracePlugin : Plugin<Project> {
override fun apply(project: Project) {
when {
project.plugins.hasPlugin("com.android.application") -> {
val extension = project.extensions.getByName ("android") /*as AppExtension*/
extension?.let {
it.registerTransform(TestTransFormO) //! method not found
println("SystracePluginTest.kt apply $it")
}
}
}
}
}

How to avoid function duplication

Lets's say i've this function:
fun createView(binding, type) {
binding.heading_text.text = type
}
And I need this exact function in 2 or more fragments, how can I handle this without duplicate the function every fragment?
You could use utility static class, something like
class MyLogger {
companion object {
fun logNumbers(num1, num2) {
Log.i("MainScreen", "${num1 + num2}")
}
}
}
MyLogger.logNumbers(1, 2)
or inline them
You can create an object with this function.
object Util {
fun createView(binding, type) {
binding.heading_text.text = type
}
And you can use it like this:
Util.createView(binding, type)

Android generalized kotlin function to return any class and take any parameter

In my network repository, I have already a generalized function like this that will work when my API returns a particular model -
override suspend fun getRandomSchoolImageFromRemote(): SchoolResult {
return generalizedFunction {
remoteInterface.getRandomImage()
}
}
override suspend fun getSchoolImageByIdFromRemote(id: String): SchoolResult {
return generalizedFunction {
remoteInterface.getSchoolImageById(id)
}
}
And my generalized function looks like this -
private inline fun generalizedFunction(block: () -> SchoolModel): SchoolResult {
if (util.checkDeviceInternet()) {
try {
val result = block()
if (result.status == "200") {
return SchoolResult.Content(result)
} else {
return SchoolResult.Error(SchoolResult.ErrorType.API_ERROR)
}
} catch (e: Exception) {
return SchoolResult.Error(SchoolResult.ErrorType.API_ERROR)
}
} else {
return SchoolResult.Error(SchoolResult.ErrorType.NO_INTERNET)
}
}
Currently my genjeralized function can return a sealed class SchoolResult and also can only take SchoolModel as parameter. However I want to have this generalized function even more generalized where it can handle any model as parameter such as ChildrenModel or event just a Unit and can return any Sealed class such as SchoolResult or ChildrenResult. Can someone help me with this?
Let's break down the question into to problems:
You want to have a generic input type
You want to have a generic output type with a constraint on it being a sealed class.
We can use generics to solve both problems which allows us to provide arguments and output type as a parameter.
For example, to solve the first problem, we can use something like this
private inline fun <INPUT> generalizedFunction(block: () -> INPUT): SchoolResult {
...
}
but this means that you don't have direct access to .status which you use to do result.status == "200". So you will need to either re-think your models or your logic inside the function to overcome this.
The second problem is quite tricky. Say we did something like this
private inline fun <INPUT, OUTPUT> generalizedFunction(block: () -> INPUT): OUTPUT {
...
}
There is no way for us to know if OUTPUT is a sealed class or not and even if it is a sealed class, there is no way to know its children such as Content and Error.
One way to go about this is to wrap your result in a more generic way. So you can do something like this for example:
sealed class Result<out T> {
data class Content<out T>(val data: T) : Result<T>()
data class Error<out T>(val errorCode: ErrorType) : Result<T>()
}
then the final outcome for a truly general method would be
private inline fun <INPUT,OUTPUT> generalizedFunction(block: () -> INPUT): Result<OUTPUT> {
if (util.checkDeviceInternet()) {
try {
val result = block()
if (result.status == "200") {
return Result.Content(result)
} else {
return Result.Error(ErrorType.API_ERROR)
}
} catch (e: Exception) {
return Result.Error(ErrorType.API_ERROR)
}
} else {
return Result.Error(ErrorType.NO_INTERNET)
}
}
where ErrorType is a more generic enum that's not part of SchoolResult.

Does Kotlin `by` keyword work for nullable delegate?

I've pretty excited by Kotlin compiler features and by by in particular - it saves time generating gelegating code:
https://kotlinlang.org/docs/reference/delegation.html
But i want delegate to be nullable and delegating code to check if it's null first and return if it is:
interface Base {
val message: String
fun print()
}
class BaseImpl(val x: Int?) : Base {
override val message = "BaseImpl: x = $x"
override fun print() { println(message) }
}
class Derived(b: Base?) : Base by b {
// This property is not accessed from b's implementation of `print`
override val message = "Message of Derived"
}
fun main() {
val b = BaseImpl(10)
val derived = Derived(b)
derived.print()
println(derived.message)
}
When compiling ^ i'm getting Type mismatch: inferred type is Base? but Base was expected.
Is it still possible with Kotlin?
To be more detailed i'd like Kotlin compiler to generate forwarding calls to wrapped impl (extWebChromeClient) in https://developer.android.com/reference/android/webkit/WebChromeClient like follows:
private WebChromeClient intWebChromeClient = new WebChromeClient()
{
#Override
public void onReceivedTitle(WebView view, String title)
{
if (extWebChromeClient != null)
{
extWebChromeClient.onReceivedTitle(view, title);
}
}
...
You can make this yourself using dynamic proxies, though I wouldn't really recommend it. Note that for non-void methods there's no way to require overriding them. The below implementation just throws exceptions for them unconditionally, but you could still call them for non-null x.
inline fun <reified T : Any> nullableProxy(x: T?): T {
val handler = InvocationHandler { _, method, args ->
if (method.returnType == Void.TYPE) {
if (x != null) {
method.invoke(x, *(args ?: arrayOf()))
}
} else
throw UnsupportedOperationException("Non-void method")
}
return Proxy.newProxyInstance(
T::class.java.classLoader,
arrayOf(T::class.java),
handler) as T
}
class Derived(b: Base?) : Base by nullableProxy(b)
This also won't perform as well as implementing methods directly would.

Error with mock methods

I try to mock some methods in the project so that when they are called, a certain value is returned.
But when you run the tests, they fall with the output:
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers! 0 matchers expected, 1 recorded:
-> at com.hodzi.stackviewer.questions.detail.QuestionDetailPresenterTest.voteTest(QuestionDetailPresenterTest.kt:69)
This exception may occur if matchers are combined with raw values:
//incorrect:
someMethod(anyObject(), "raw String"); When using matchers, all arguments have to be provided by matchers. For example:
//correct:
someMethod(anyObject(), eq("String by matcher"));
If you run the same code in debug mode and run through all the lines, then when you call shared.getToken (), the value that we specified is returned. But with normal startup, tests fall on this line.
Code:
import com.hodzi.stackviewer.questions.QuestionsInteractor
import com.hodzi.stackviewer.utils.Shared
import com.hodzi.stackviewer.utils.Vote
import org.junit.BeforeClass
import org.junit.Test
import org.mockito.ArgumentMatchers
import org.mockito.Mockito
internal class QuestionDetailPresenterTest {
companion object {
lateinit var presenter: QuestionDetailPresenter
lateinit var view: QuestionDetailView
#BeforeClass #JvmStatic
fun setUp() {
val questionsInteractor: QuestionsInteractor =
Mockito.mock(QuestionsInteractor::class.java)
val shared: Shared =
Mockito.mock(Shared::class.java)
Mockito.`when`(shared.getToken()).thenReturn("23")
// Mockito.doReturn("23").`when`(shared).getToken()
view = Mockito.mock(QuestionDetailView::class.java)
presenter = QuestionDetailPresenter(questionsInteractor, shared)
}
}
#Test
fun voteTest() {
presenter.vote(ArgumentMatchers.anyInt(), Vote.QUESTION_DOWN)
Mockito.verify(view).goToAuth()
}
}
Shared:
interface Shared {
companion object {
const val KEY_TOKEN: String = "keyToken"
}
fun getToken(): String
fun saveToken(token: String?)
}
Presenter:
class QuestionDetailPresenter(val questionsInteractor: QuestionsInteractor, val shared: Shared) :
BasePresenter<QuestionDetailView>() {
lateinit var question: Question
fun vote(id: Int, vote: Vote) {
print(vote)
if (Strings.isEmptyString(shared.getToken())) {
view?.goToAuth()
return
}
val observable: Observable<out Data> = when (vote) {
Vote.ANSWER_UP -> {
questionsInteractor.answerUpVote(id, shared.getToken())
}
Vote.ANSWER_DOWN -> {
questionsInteractor.answerDownVote(id, shared.getToken())
}
Vote.QUESTION_UP -> {
questionsInteractor.questionUpVote(id, shared.getToken())
}
Vote.QUESTION_DOWN -> {
questionsInteractor.questionDownVote(id, shared.getToken())
}
}
baseObservableData(observable,
{ data ->
run {
Log.d(Const.LOG_TAG, "success")
}
},
{ throwable ->
run {
Log.d(Const.LOG_TAG, "error")
}
}
)
}
}
Thanks!
There's nothing wrong with your mocking of shared, I think the problem is with:
presenter.vote(ArgumentMatchers.anyInt(), Vote.QUESTION_DOWN)
Just use a real Int instead of the ArgumentMatchers.anyInt().
Like
presenter.vote(0, Vote.QUESTION_DOWN)
Matchers are used when matching arguments on a mocked object, for example
val calulator = (mock with Mockito)
when(calculator.divideByTwo(anyInt()).thenReturn(1)
would mean calculator.divideByTwo(int: Int) returns 1 when called with any Int.
When calling methods of real objects to test them (like you do with your presenter), you use real parameters.

Categories

Resources