Java <-> Scala interop: прозрачное преобразование Списка и Карты

Я использую его для реализации кэша, где неиспользованные записи автоматически собраны "мусор":

class Cache<TKey,TValue> : IEnumerable<KeyValuePair<TKey,TValue>>
{ Dictionary<TKey,WeakReference> dict = new Dictionary<TKey,WeakReference>();

   public TValue this[TKey key]
    { get {lock(dict){ return getInternal(key);}}
      set {lock(dict){ setInteral(key,value);}}     
    }

   void setInteral(TKey key, TValue val)
    { if (dict.ContainsKey(key)) dict[key].Target = val;
      else dict.Add(key,new WeakReference(val));
    } 


   public void Clear() { dict.Clear(); }

   /// <summary>Removes any dead weak references</summary>
   /// <returns>The number of cleaned-up weak references</returns>
   public int CleanUp()
    { List<TKey> toRemove = new List<TKey>(dict.Count);
      foreach(KeyValuePair<TKey,WeakReference> kv in dict)
       { if (!kv.Value.IsAlive) toRemove.Add(kv.Key);
       }

      foreach (TKey k in toRemove) dict.Remove(k);
      return toRemove.Count;
    }

    public bool Contains(string key) 
     { lock (dict) { return containsInternal(key); }
     }

     bool containsInternal(TKey key)
      { return (dict.ContainsKey(key) && dict[key].IsAlive);
      }

     public bool Exists(Predicate<TValue> match) 
      { if (match==null) throw new ArgumentNullException("match");

        lock (dict)
         { foreach (WeakReference weakref in dict.Values) 
            { if (   weakref.IsAlive 
                  && match((TValue) weakref.Target)) return true;
         }  
      }

       return false;
     }

    /* ... */
   }
33
задан Peter Mortensen 11 June 2013 в 14:11
поделиться

2 ответа

Поверьте мне; вам не нужно прозрачное преобразование туда и обратно. Это именно то, что пытались сделать функции scala.collection.jcl.Conversions . На практике это вызывает много головной боли.

Корень проблемы с этим подходом заключается в том, что Scala автоматически вводит неявные преобразования по мере необходимости, чтобы вызов метода работал. Это может иметь очень печальные последствия. Например:

import scala.collection.jcl.Conversions._

// adds a key/value pair and returns the new map (not!)
def process(map: Map[String, Int]) = {
  map.put("one", 1)
  map
}

Этот код не будет полностью неуместным для тех, кто плохо знаком с фреймворком коллекций Scala или даже с концепцией неизменяемых коллекций. К сожалению, это совершенно неверно. Результатом этой функции является такая же карта . Вызов put запускает неявное преобразование в java.util.Map , который с радостью принимает новые ценности и быстро отбрасывается. Исходная карта не изменена (поскольку она действительно неизменна).

Хорхе Ортис выразился лучше всего, когда говорит, что вы должны определять неявные преобразования только для одной из двух целей:

  • Добавление членов (методы, поля и т. д.). Эти преобразования должны быть в новый тип , не связанный ни с чем другим в области видимости.
  • «Исправление» нарушенной иерархии классов. Таким образом, если у вас есть несколько типов A и B , которые не связаны между собой. Вы можете определить преобразование A => B if и только , если вы предпочитаете иметь A <: B ( <: означает «подтип»).

Начиная с java.util. Карта , очевидно, не новый тип, не связанный ни с чем в нашей иерархии, мы не можем подпадать под первую оговорку. Таким образом, наша единственная надежда состоит в том, что наше преобразование Map [A, B] => java.util.Map [A, B] будет подходить для второго. Однако для Scala Map совершенно бессмысленно наследовать от java.util.Map . Это действительно полностью ортогональные интерфейсы / черты. Как показано выше, попытка игнорировать эти рекомендации почти всегда приводит к странному и неожиданному поведению.

На самом деле методы javautils asScala и asJava были разработаны для решения этой точной задачи. проблема. Существует неявное преобразование (на самом деле их несколько) в javautils из Map [A, B] => RichMap [A, B] . RichMap - это совершенно новый тип, определенный javautils, поэтому его единственная цель - добавить элементы в Map . В частности, он добавляет метод asJava , который возвращает карту-оболочку, которая реализует java.util.Map и делегирует исходный экземпляр Map . Это делает процесс более явным и гораздо менее подверженным ошибкам.

Другими словами, использование asScala и asJava является наилучшей практикой. Пройдя оба этих пути независимо в производственном приложении, я могу из первых рук сказать вам, что подход javautils намного безопаснее и с ним проще работать. Не пытайтесь обойти его защиту только ради спасения 8 персонажей!

поэтому его единственная цель - добавить участников в Map . В частности, он добавляет метод asJava , который возвращает карту-оболочку, которая реализует java.util.Map и делегирует исходный экземпляр Map . Это делает процесс более явным и гораздо менее подверженным ошибкам.

Другими словами, использование asScala и asJava является наилучшей практикой. Пройдя оба этих пути независимо в производственном приложении, я могу из первых рук сказать вам, что подход javautils намного безопаснее и с ним проще работать. Не пытайтесь обойти его защиту только ради спасения 8 персонажей!

поэтому его единственная цель - добавить участников в Map . В частности, он добавляет метод asJava , который возвращает карту оболочки, которая реализует java.util.Map и делегирует исходный экземпляр Map . Это делает процесс более явным и гораздо менее подверженным ошибкам.

Другими словами, использование asScala и asJava является наилучшей практикой. Пройдя оба этих пути независимо в производственном приложении, я могу из первых рук сказать вам, что подход javautils намного безопаснее и с ним проще работать. Не пытайтесь обойти его защиту только ради спасения 8 персонажей!

который возвращает карту-оболочку, которая реализует java.util.Map и делегирует исходный экземпляр Map . Это делает процесс более явным и гораздо менее подверженным ошибкам.

Другими словами, использование asScala и asJava является наилучшей практикой. Пройдя оба этих пути независимо в производственном приложении, я могу из первых рук сказать вам, что подход javautils намного безопаснее и с ним проще работать. Не пытайтесь обойти его защиту только ради спасения 8 персонажей!

который возвращает карту-оболочку, которая реализует java.util.Map и делегирует исходный экземпляр Map . Это делает процесс более явным и гораздо менее подверженным ошибкам.

Другими словами, использование asScala и asJava является наилучшей практикой. Пройдя оба этих пути независимо в производственном приложении, я могу из первых рук сказать вам, что подход javautils намного безопаснее и с ним проще работать. Не пытайтесь обойти его защиту только ради спасения 8 персонажей!

использование asScala и asJava является наилучшей практикой. Пройдя оба этих пути независимо в производственном приложении, я могу из первых рук сказать вам, что подход javautils намного безопаснее и с ним проще работать. Не пытайтесь обойти его защиту только ради спасения 8 персонажей!

использование asScala и asJava является наилучшей практикой. Пройдя оба этих пути независимо в производственном приложении, я могу из первых рук сказать вам, что подход javautils намного безопаснее и с ним проще работать. Не пытайтесь обойти его защиту только ради спасения 8 персонажей!

65
ответ дан 27 November 2019 в 18:02
поделиться

Вот несколько быстрых примеров использования библиотеки Scalaj-collection Хорхе Ортиса :

import org.scala_tools.javautils.Implicits._

val sSeq = java.util.Collections.singletonList("entry") asScala
// sSeq: Seq[String] 
val sList = sSeq toList // pulls the entire sequence into memory
// sList: List[String]
val sMap = java.util.Collections.singletonMap("key", "value") asScala
// sMap: scala.collection.Map[String, String]

val jList = List("entry") asJava
// jList: java.util.List[String]
val jMap = Map("key" -> "value") asJava
// jMap: java.util.Map[String, String]

проект javautils доступен в центральном репозитории maven

3
ответ дан 27 November 2019 в 18:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: