Generic Static Constant - android

It's hard to explain but I basically want to be able to have an interface which my subclasses need to implement, which mandates a static constant. How would I achieve this in Kotlin?
I have the following class/interface:
interface BaseFragmentInterface {
val TAG: String
}
class BaseFragment: Fragment() {
/* this, of course, doesn't compile right now: */
companion object: BaseFragmentInterface {}
func<T: BaseFragment> push(fragment: T) {
/* Here I want to access TAG */
Log.d(T.TAG, "Push Fragment")
}
}
My subclasses need to be required to implement the interface. And my base companion object would need to be somehow abstract.
Any idea how to achieve this?

Let me precise few things.
My subclasses need to be required to implement the interface.
There's no way to achieve this, you can't force a subclass' companion object to implement your interface since the companion object of a class is not related to the companion object of its superclass.
And my base companion object would need to be somehow abstract.
Since the companion object is an object, it can't be abstract, because an object can't be abstract too.
Said that, you have two options to still achieve your desired behavior:
Use TAG as a non-static final property inside BaseFragment:
abstract class BaseFragment : Fragment() {
protected abstract val TAG: String
fun <T : BaseFragment> push(fragment: T) {
/* Here I want to access TAG */
Log.d(TAG, "Push Fragment")
}
}
class FragmentImpl : BaseFragment() {
override val TAG: String = "fragment-impl"
}
Use companion objects to hold the tag, make the type T of push() as reified and access the TAG property inside the companion object of T using the reflection.

Related

Type cast / make genric viewModel to be passed as parameter

I am working on a custom dialog fragment, that is being used/ called from two different views having different viewModels. Instead of passing two separate viewModels in the constructor parameter of Dialog class as,
class CustomeDialog(var viewModel1: ViewModelA ?= null, var viewModel2 : ViewModelB ?= null) : DialogFragment()
I need to ask/ figure out a way where I could just set < T> kind of parameter to dialog so I could just type caste any viewModel to it, I want.
something like this,
class CustomDialog<T:ViewModel> : DialogFragment()
and in code, it would be something like
val mdialog1: CustomeDialog by lazy { CustomeDialog(viewModel as ViewModelA) }
and also
val mdialog2: CustomeDialog by lazy { CustomeDialog(viewMode2 as ViewModelB) }
You can create a secondary constructor in the generic class that takes in a generic ViewModel parameter:
class CustomeDialog<T : ViewModel>() : DialogFragment() {
constructor(viewmodel: T) : this()
}
And the usage the same as you did:
lateinit var viewModel: ViewModel
val mdialog1: CustomeDialog<ViewModelA> by lazy { CustomeDialog(viewModel as ViewModelA) }
lateinit var viewModel2: ViewModelA
val mdialog2: CustomeDialog<ViewModelA> by lazy { CustomeDialog(viewModel2) }
UPDATE:
how to initialize viewModel in dialog based on the type. eg. if VM1 is passed in constructor, then var dialogViewModel = WHAT??,
It's requested to have a Dialog with a generic ViewModel, so its type is Generic as it's unknown till it's instantiated.
yeah i need a local var dialogViewModel which is generic, as i mentioned, whole logic is dependent on this dvm
You can initialize it in the secondary constructor:
class CustomDialog<T : ViewModel>() : DialogFragment() {
lateinit var dialogViewModel: T
constructor(viewmodel: T) : this() {
dialogViewModel = viewmodel
}
}
This strategy cannot work. The OS recreates your Fragment using reflection and its empty constructor. It can restore state to the replacement Fragment using Bundle values, but class types are not a valid type of data for a Bundle.
Closest I can come up with is to make it an abstract class, and then create simple subclasses that have concrete types.
abstract class CustomDialog<T: ViewModel>(viewModelType: KClass<out T>): DialogFragment() {
val viewModel: T by createViewModelLazy(viewModelType, { viewModelStore })
}
class CustomDialogA: CustomDialog<ViewModelA>(ViewModelA::class)
class CustomDialogB: CustomDialog<ViewModelB>(ViewModelB::class)

Difference between object / private constructor / companion object in Kotlin?

Please don't blame me. Im just moving from Java to Kotlin. I'm trying to create a singleton as a regular java way with help of Singleton.getInstance().someMethod() and have found that in Kotlin there are several different things you can use:
Object (separate file) object Singleton
Companion object companion object Factory {}
Private constructor
class Singleton private constructor()
So can you please help me and explain where we can you what type?
Objects in Kotlin are like static classes in Java. They are usually used to construct the singleton pattern:
object Singleton
The equivalent in Java would be:
public static class Singleton{}
The companion object is used in cases (as your companion object name states) where you have to apply the Factory pattern or the static factory pattern.
Let's suppose we have this in Java:
public class Fragment(){
private Fragment(){}
public static Fragment newInstance(){
return new Fragment();
}
}
The equivalent of that in Kotlin would be:
class Fragment private constructor(){
companion object{
fun newInstance() = Fragment()
}
}
The companion object is also an object but through the word companion is just telling JVM that the one class in which this object is, has access to everything inside it.
Therefore if you try to call it from Java code, it would be something like this:
Fragment.Companion.newInstance()
The above example actually also fits for private constructor.
Basically, even in Java, when you don't need to access the constructor directly, just mark the constructor as private and use a static factory method.
Regarding your question, with the information provided above:
To achieve
Singleton.getInstance().someMethod()
with exactly this call, you have to do this:
class Singleton private constructor(){
companion object{
fun getInstance() = Singleton()
fun someMethod(){ /* Your implement here */}
}
}
However that's not too sophisticated in Kotlin style.
Just do:
object Singleton{
fun someMethod(){ /* Your method here */}
}
Then just call it:
Singleton.myMethod()
EDIT: Regarding your question of SharedPreferences I don't suggest to use an object for that. You need the constructor for the context and perhaps shared preferences mode. Therefore I would go with something like this (assuming that you are using dagger since you mentioned it in comment):
class SharedPreferencesHelper #Inject constructor(val context: Context, val mode: Int) // not sure about the mode type but check the docs {
private lateinit var sharedPreferences: SharedPreferences
private lateinit var sharedPreferencesEditor: SharedPreferences.Editor
init{
sharedPreferences = context.getSharedPreferences("filename", mode)
sharedPreferencesEditor = sharedPreferences.edit()
}
}
Then you just call it in any constructor you need it.
Or:
class SharedPreferencesHelper private constructor(){
private lateinit var sharedPreferences: SharedPreferences
private lateinit var sharedPreferencesEditor: SharedPreferences.Editor
companion object {
fun startSharedPrefs(context: Context, fileName: String, mode: Int) = SharedPreferencesHelper().apply{
sharedPreferences = context.getSharedPreferences(fileName, mode)
sharedPreferencesEditor = sharedPreferences.edit()
}
}
}
Then start it in a dagger module:
#Module
object SharedPrefsModule{
#Singleton
#Provides
fun provideSharedPreferences(application: Application) =
SharedPreferencesHelper.startSharedPrefs(application, "fileName", MODE_PRIVATE)
}
Then call the dependency wherever you need it
object Singleton
Thread safe singleton
Not an expression
Cannot be used on the right hand side of an assignment statement.
Object declaration's initialization is thread-safe and done at first access
Can have supertypes
Object declarations can't be local (i.e. be nested directly inside a function)
Can be nested into other object declarations or non-inner classes
companion object Factory {}
Members of the companion object can be called by using simply the class name (hosting companion) as the qualifier
The name of the companion object can be omitted
Members of companion objects look like static members in other languages, at runtime those are still instance members of real objects
A companion object is initialized when the corresponding class is loaded (resolved), matching the semantics of a Java static initializer
class Singleton private constructor()
I don't think you need that for singleton in kotlin as kotlin already provides good singleton option out of the box, however as stated by other SO user here private constructor is serving the same purpose in kotlin as in, for example, java - to prevent instantiation. As a second thought, if you think of creating utils like classes in kotlin, please better consider using extension functions.
PS.: it should be pretty obvious so I'll mention it - 99% of the above is brutally copy-pasted from https://kotlinlang.org/docs/reference/object-declarations.html - may be it has better chances to be more searchable here:)
If you need a singleton - a class that only has got one instance - you can declare the class in the usual way, but use the object keyword instead of class.
If you need a function or a property to be tied to a class rather than to instances of it (similar to #staticmethod in Python), you can declare it inside a companion object.
Private constructors are used to prevent creating instances of a class when there are no instance fields or methods, such as the Math class, or when a method is called to obtain an instance of a class.

Can I execute inherited static functions from a BaseClass from a ChildClass and get the childs properties?

I have a few cases where I want to add static functions or values in a base class so that I can use them in all subclasses that inherits from it.
One such case is when i want to create generic tags for each class to use in data mapping as a key, like when i want to find fragments or pass data between activities.
For example:
open class BaseClass(){
companionObject{
val TAG: String = this.javaClass.simpleName
}
}
class ChildClass: BaseClass()
class Main: Activity(){
fun startActivity(){
val intent = Intent(this, ChildClass::class.java)
intent.putExtra(ChildClass.TAG, data)
startActivity(intent)
finish()
}
}
Can this be done or am I forced to create an companion object for each class?
I don't know a solution with companions. But you could use a global reified inline function for the specific use case, you mentioned in your question:
open class BaseClass()
class ChildClass: BaseClass()
inline fun <reified T> tagOf() = T::class.java.simpleName
fun main(args: Array<String>) {
println(tagOf<BaseClass>())
println(tagOf<ChildClass>())
}
Hm... I think, you can't do it. As mentioned in this article: https://proandroiddev.com/a-true-companion-exploring-kotlins-companion-objects-dbd864c0f7f5
companion object is really a public static final class in your BaseClass. So, I think, you can't do this.

Kotlin typealias of generic supertype with bound type parameter not working for inheritance

TL;DR why does this work:
interface SomeInterface
interface Generic <T : SomeInterface> {}
class Self : Generic<Self>, SomeInterface
And this doesn't:
interface SomeInterface
interface Generic <T : SomeInterface> {}
typealias Specified = Generic<Self>
class Self : Specified, SomeInterface
Error: Type argument is not within its bounds: should be subtype of
'SomeInterface'
Error says it isn't right subtype, but it is!
The use case of such typealias comes from real-life code, where a superclass has 5 type parameters, each with rather long name, and I don't want to pollute class header with unnecessary spam. Any ideas? I'm using kotlin 1.2.51.
--- Original problem ---
MVP interfaces:
interface MVPComponent // dagger component
interface MVPComponentHolder<C : MVPComponent> // implementing class must provide component
interface MVPArgs // args passed from view to presenter on attach
interface MVPView<A : MVPArgs> // MVP View, must provide Args for presenter
interface MVPPresenter<T : MVPView<A>, A : MVPArgs, U : MVPUseCase>
interface MVPUseCase // selected API methods to use in presenter
Base Fragment for MVP:
abstract class MVPFragment<F, V, P, A, C>
: Fragment(), MVPComponentHolder<C>
where F : V,
V : MVPView<A>,
P : MVPPresenter<V, A, *>,
C : MVPComponent<V>,
A : MVPArgs {
// lots of MVP logic
}
MVP contract:
interface ExampleMVP {
data class Args(/* ... */) : MVPArgs
interface View : MVPView<Args> {
//...
}
interface Presenter : MVPPresenter<View, Args, UseCase> {
//...
}
interface UseCase : MVPUseCase<View> {
//...
}
}
Final fragment:
class ExampleFragment : MVPFragment<
ExampleFragment,
ExampleMVP.View,
ExampleMVP.Presenter,
ExampleMVP.Args,
ExampleMVP>(), ExampleMVP.View {
// final fragment logic
}
But I would like to use following syntax:
private typealias SuperFragment = MVPFragment<
ExampleFragment,
ExampleMVP.View,
ExampleMVP.Presenter,
ExampleMVP.Args,
ExampleMVP>
class ExampleFragment : SuperFragment(), ExampleMVP.View {
// final fragment logic
}
The reason I pass ExampleFragment as type argument of MVPFragment is because Dagger 2 must inject directly into target class (in this case not just Fragment or MVPFragment, but ExampleFragment), or injection code will not be generated.
The injection in MVPFragment looks the following way:
#CallSuper
override fun onAttach(context: Context) {
super.onAttach(context)
component.injectIntoView(this as F) // must be target fragment type (here F)
}
The problem is the circular dependency introduced by:
class Self : Specified, SomeInterface
If Self does not inherit from Specified it works.
The changed example looks like this.
interface SomeInterface
interface Generic <T : SomeInterface> {}
typealias Specified = Generic<Self>
class Self : SomeInterface
As for your original problem. I don`t think you can achieve this fully but the number of type parameters necessary could be reduced like this:
class ExampleFragment : SuperFragment<ExampleFragment>(), ExampleMVP.View {
// final fragment logic
}
private typealias SuperFragment<T> = MVPFragment<
T,
ExampleMVP.View,
ExampleMVP.Presenter,
ExampleMVP.Args,
ExampleMVP<ExampleMVP.View, ExampleMVP.Args>>

Companion Objects in Kotlin Interfaces

I am trying to make a interface Parcelable, as such I need a interface like this
interface AB : Parcelable {
companion object {
val CREATOR : Parcelable.Creator<AB>
}
}
and my two classes A and B looking like
data class A (...): Parcelable{
...
companion object {
val CREATOR : Parcelable.Creator<AB> = object : Parcelable.Creator<AB> {
override fun newArray(size: Int): Array<AB?> {
return arrayOfNulls(size)
}
override fun createFromParcel(parcel: Parcel): AB {
return A(parcel)
}
}
}
I am struggling to implement such a interface in kotlin. It seems the interface class does not allow for the CREATOR
Perhaps I am taking the wrong approach,
I have a parcelable that contains a list of classes that are either A or B
so I am doing
parcel.readTypedList(this.list, AB.CREATOR)
I require that the list be either A or B and that is why I am using an interface.
Anyone have any advice or a possible solution?
In Kotlin, an interface can have a companion object but it is not part of the contract that must be implemented by classes that implement the interface. It is just an object associated to the interface that has one singleton instance. So it is a place you can store things, but doesn't mean anything to the implementation class.
You can however, have an interface that is implemented by a companion object of a class. Maybe you want something more like this:
interface Behavior {
fun makeName(): String
}
data class MyData(val data: String) {
companion object: Behavior { // interface used here
override fun makeName(): String = "Fred"
}
}
Note that the data class does not implement the interface, but its companion object does.
A companion object on an interface would be useful for storing constants or helper functions related to the interface, such as:
interface Redirector {
fun redirectView(newView: String, redirectCode: Int)
companion object {
val REDIRECT_WITH_FOCUS = 5
val REDIRECT_SILENT = 1
}
}
// which then can be accessed as:
val code = Redirector.REDIRECT_WITH_FOCUS
By convention classes implementing the Parcelable interface must also have a non-null static field called CREATOR of a type that implements the Parcelable.Creator interface.
You need to annotate CREATOR property with #JvmField annotation to expose it as a public static field in containing data class.
Also you can take a look at https://github.com/grandstaish/paperparcel — an annotation processor that automatically generates type-safe Parcelable wrappers for Kotlin and Java.

Categories

Resources